X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F02_dom%2F04_XBoxModel.js;h=ad62c8de7a7127776eae6cd8746635e09073bc74;hb=6b28a86cc49680dac50278ff5617bfe7a3d98613;hp=d6b1cb9631a8e60204f025f42aeecc0b9ddf8a13;hpb=d9cca45398f61025472f2858818519562a746e61;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/02_dom/04_XBoxModel.js b/0.6.x/js/02_dom/04_XBoxModel.js index d6b1cb9..ad62c8d 100644 --- a/0.6.x/js/02_dom/04_XBoxModel.js +++ b/0.6.x/js/02_dom/04_XBoxModel.js @@ -1,72 +1,56 @@ -X.Dom.BoxModel = { - CONTENT_BOX : 1, - PADDING_BOX : 2, - BORDER_BOX : 3, - - defaultBoxModel : 0, - boxSizingEnabled : false, +var X_Node_BoxModel = { + CONTENT_BOX : 1, + PADDING_BOX : 2, + BORDER_BOX : 3 + }, - // TODO: offsetLeft, offsetTop の基準位置 - absoluteOffset : 0, + X_Node_BoxModel_defaultBoxModel, - vScrollbarSize : 0, - hScrollbarSize : 0 -}; - - - -X.ViewPort.listenOnce( X_TEMP.SYSTEM_EVENT_INIT, function(){ + X_Node_BoxModel_boxSizingEnabled, + // TODO: offsetLeft, offsetTop の基準位置 + X_Node_BoxModel_absoluteOffset; - var node = Node._systemNode, +X_ViewPort[ 'listenOnce' ]( X_EVENT_INIT, function(){ + var node = X_Node_systemNode; - // http://jsdo.it/imaya/kTYg - body = document.body, - defaultOverflow = document.body.style.overflow, - width, height; - - body.style.overflow = 'hidden'; - w = body.clientWidth; - h = body.clientHeight; - - body.style.overflow = 'scroll'; - w -= body.clientWidth; - h -= body.clientHeight; - - if( !w ) w = body.offsetWidth - body.clientWidth; - if( !h ) h = body.offsetHeight - body.clientHeight; - body.style.overflow = defaultOverflow; - - X.Dom.BoxModel.vScrollbarSize = w; - X.Dom.BoxModel.hScrollbarSize = h; - if( h <= 0 ){ // ie6, ie11, firefox で 負の値が返る - console.log( 'invalid hScrollbarSize: ' + h ); - X.Dom.BoxModel.hScrollbarSize = w; - }; - // + node[ 'cssText' ]( 'width:10px;padding:1px;border:2px solid #0;margin:4px;' ); - node.cssText( 'width:10px;padding:1px;border:2px solid #0;margin:4px;' ); + X_Node_BoxModel_defaultBoxModel = node[ 'width' ]() === 10 ? + X_Node_BoxModel.BORDER_BOX : + X_Node_BoxModel.CONTENT_BOX; - X.Dom.BoxModel.defaultBoxModel = node.width() === 10 ? - X.Dom.BoxModel.BORDER_BOX : - X.Dom.BoxModel.CONTENT_BOX; - - if( X.Dom.BoxModel.defaultBoxModel === X.Dom.BoxModel.CONTENT_BOX ){ - X.Dom.BoxModel.boxSizingEnabled = node.cssText( 'width:10px;padding:1px;border:2px solid #0;margin:4px;' + + if( X_Node_BoxModel_defaultBoxModel === X_Node_BoxModel.CONTENT_BOX ){ + X_Node_BoxModel_boxSizingEnabled = node[ 'cssText' ]( 'width:10px;padding:1px;border:2px solid #0;margin:4px;' + 'box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-o-box-sizing:border-box;-ms-box-sizing:border-box;' ) - .width() === 10; + [ 'width' ]() === 10; + }; + + /* + * 古い Gekco、 Presto、 WebKit では影がレイアウトに影響します。たとえば、width が 100% のボックスに外向きの box-shadow を指定すると、横スクロールバーが表示されてしまいます。 + * TODO boxShadow が有効な要素に対して offsetWidth 等の補正(?) + */ + if( X_Node_CSS_Support[ 'boxShadow' ] && + node[ 'cssText' ]( + X_Node_CSS_uncamelize( X_Node_CSS_VENDER_PREFIX[ 'boxShadow' ] ) + ':10px 10px 0 0 #000;width:10px;' + )[ 'width' ]() !== 10 + ){ + console.log( node[ 'cssText' ]() + node[ 'width' ]() ); + X_Node_CSS_Support[ 'boxShadowLayoutBug' ] = true; }; + // padding // border // margin // top - X.Dom.BoxModel.absoluteOffset = - node.cssText( 'position:absolute;top:0;left:0;margin:1px;border:2px solid #000;padding:4px;' ) - .append( '
' ) - .firstChild().cssText( 'position:absolute;top:8px;left:8px;margin:16px;border:32px solid #666;padding:64px;' ) - .y(); + X_Node_BoxModel_absoluteOffset = + node[ 'cssText' ]( 'position:absolute;top:0;left:0;margin:1px;border:2px solid #000;padding:4px;' ) + [ 'append' ]( '
' ) + [ 'firstChild' ]() + [ 'cssText' ]( 'position:absolute;top:8px;left:8px;margin:16px;border:32px solid #666;padding:64px;' ) + [ 'y' ](); - node.cssText( '' ).empty(); + node[ 'cssText' ]( '' )[ 'empty' ](); }); /* -------------------------------------- @@ -77,267 +61,240 @@ X.ViewPort.listenOnce( X_TEMP.SYSTEM_EVENT_INIT, function(){ * getBoxObjectFor * getBoundingClientRect */ -Node.prototype.width = function(){ - if( !this.parent ){// todo : _state で tree に所属しているか?判定 - console.log( 'xnode.width() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.width() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'width' ); - return this._rawObject.offsetWidth; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).offsetWidth; - } else { - - }; + +function X_Node_BoxModel_mesure( that, name ){ + var flags = that[ '_flags' ], elm; + + if( ( ( flags & X_NodeFlags_IN_TREE ) === 0 ) || ( flags & X_NodeFlags_STYLE_IS_DISPLAY_NONE ) ) return 0; + + X_Node_updateTimerID && X_Node_startUpdate(); + + elm = that[ '_rawObject' ] || X_Node__ie4getRawNode && X_Node__ie4getRawNode( that ); + return elm ? elm[ name ] : 0; }; -Node.prototype.height = function(){ - if( !this.parent ){ - console.log( 'xnode.height() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.height() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'height' ); - return this._rawObject.offsetHeight; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).offsetHeight; - } else { - - }; +/** + * 要素の幅。elm.offsetWidth + * @alias Node.prototype.width + * @return {number} + * @example node.width(); + */ +function X_Node_width(){ + return X_Node_BoxModel_mesure( this, 'offsetWidth' ); + + // TODO width : length + overflow : hidden ならそれを返す? <- block or inline + // TODO TextNode どうする? + //if( X_UA_DOM.IE4 ) return elm ? elm.style.pixelWidth : 0; + //return elm ? elm.offsetWidth : 0; }; -Node.prototype.clientWidth = function(){ - if( !this.parent ){// todo : _state で tree に所属しているか?判定 - console.log( 'xnode.width() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.width() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'width' ); - return this._rawObject.clientWidth; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).clientWidth; - } else { - - }; +/** + * 要素の高さ。elm.offsetHeight + * @alias Node.prototype.height + * @return {number} + * @example node.height(); + */ +function X_Node_height(){ + return X_Node_BoxModel_mesure( this, 'offsetHeight' ); + + // this[ 'css' ]( X_Node_CSS_Unit.px, 'height' ); + //if( X_UA_DOM.IE4 ) return elm ? elm.style.pixelHeight : 0; + //return elm ? elm.offsetHeight : 0; }; -Node.prototype.clientHeight = function(){ - if( !this.parent ){ - console.log( 'xnode.height() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.height() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'height' ); - return this._rawObject.clientHeight; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).clientHeight; - } else { - - }; +/** + * 要素のコンテンツ領域の幅。elm.clientWidth + * @alias Node.prototype.clientWidth + * @return {number} + * @example node.clientWidth(); + */ +function X_Node_clientWidth(){ + return X_Node_BoxModel_mesure( this, 'clientWidth' ); + + // this[ 'css' ]( X_Node_CSS_Unit.px, 'width' ); + //return elm ? elm.clientWidth : 0; }; -Node.prototype.scrollWidth = function(){ - if( !this.parent ){// todo : _state で tree に所属しているか?判定 - console.log( 'xnode.width() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.width() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'width' ); - return this._rawObject.scrollWidth; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).scrollWidth; - } else { - - }; +/** + * 要素のコンテンツ領域の高さ。elm.clientHeight + * @alias Node.prototype.clientHeight + * @return {number} + * @example node.clientHeight(); + */ +function X_Node_clientHeight(){ + return X_Node_BoxModel_mesure( this, 'clientHeight' ); + + // this[ 'css' ]( X_Node_CSS_Unit.px, 'height' ); + //return elm ? elm.clientHeight : 0; }; -Node.prototype.scrollHeight = function(){ - if( !this.parent ){ - console.log( 'xnode.height() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.height() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'height' ); - return this._rawObject.scrollHeight; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).scrollHeight; - } else { - - }; +/** + * 要素のスクロール領域の幅。elm.scrollWidth + * @alias Node.prototype.scrollWidth + * @return {number} + * @example node.scrollWidth(); + */ +function X_Node_scrollWidth(){ + return X_Node_BoxModel_mesure( this, 'scrollWidth' ); + + // this[ 'css' ]( X_Node_CSS_Unit.px, 'width' ); + //return elm ? elm.scrollWidth : 0; }; -Node.prototype.scrollLeft = function(){ - if( !this.parent ){// todo : _state で tree に所属しているか?判定 - console.log( 'xnode.scrollLeft() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.scrollLeft() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'width' ); - return this._rawObject.scrollLeft; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).scrollLeft; - } else { - - }; +/** + * 要素のスクロール領域の高さ。elm.scrollHeight + * @alias Node.prototype.scrollHeight + * @return {number} + * @example node.scrollHeight(); + */ +function X_Node_scrollHeight(){ + return X_Node_BoxModel_mesure( this, 'scrollHeight' ); }; -Node.prototype.scrollTop = function(){ - if( !this.parent ){// todo : _state で tree に所属しているか?判定 - console.log( 'xnode.scrollTop() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.scrollTop() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'width' ); - return this._rawObject.scrollTop; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).scrollTop; - } else { - - }; +/** + * 要素のスクロール位置。elm.scrollLeft + * @alias Node.prototype.scrollLeft + * @return {number} + * @example node.scrollLeft(); + */ +function X_Node_scrollLeft(){ + return X_Node_BoxModel_mesure( this, 'scrollLeft' ); +}; + +/** + * 要素のスクロール位置。elm.scrollTop + * @alias Node.prototype.scrollTop + * @return {number} + * @example node.scrollTop(); + */ +function X_Node_scrollTop(){ + return X_Node_BoxModel_mesure( this, 'scrollTop' ); }; /* -------------------------------------- * x, y * position:absolute かつ x か y が設定されていたら、再描画しないで css オブジェクトから計算した値を返す。 float は? - * position:absolute の指定で自動で top,left を補う必要あり? -> X.Dom.Style + * position:absolute の指定で自動で top,left を補う必要あり? -> X.Node.CSS * 親要素 border 外側からの値。 IE, Firefox, Safari, Chrome の offsetLeft/Topでは、border 内側なので補正する。 * transformX, Y は加える? アニメーション中は? + * + * http://www.din.or.jp/~hagi3/JavaScript/JSTips/DHTML/ProbIE5.htm#StyleObject */ -// X.Dom.Style.transform, -Node.prototype.x = function(){ - if( !this.parent ){ - console.log( 'xnode.x() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.x() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'left' ); - // this.css( X.Dom.Style.Unit.px, 'translateX' ); - return this._rawObject.offsetLeft; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).offsetLeft; - } else { - - }; -}; +// TODO X_Node_CSS_transform, -Node.prototype.y = function(){ - if( !this.parent ){ - console.log( 'xnode.y() : no parent' ); - return 0; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.y() : not belong tree.' ); - return 0; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; - if( X_UA_DOM.W3C ){ - // this.css( X.Dom.Style.Unit.px, 'top' ); - // this.css( X.Dom.Style.Unit.px, 'transisitonY' ); - return this._rawObject.offsetTop; - } else - if( X_UA_DOM.IE4 ){ - return ( this._rawObject || this._ie4getRawNode() ).offsetTop; - } else { - - }; +/** + * 要素の親要素に対する位置。offsetLeft + * @alias Node.prototype.x + * @return {number} + * @example node.x(); + */ +function X_Node_x(){ + return X_Node_BoxModel_mesure( this, 'offsetLeft' ); + + // this[ 'css' ]( X_Node_CSS_Unit.px, 'left' ); + // this[ 'css' ]( X_Node_CSS_Unit.px, 'translateX' ); + //if( X_UA_DOM.IE4 ) return elm ? elm.style.pixelLeft : 0; + //return elm ? elm.offsetLeft : 0; }; -Node.prototype.offset = function( /* xnodeParent */ ){ - var x = 0, y = 0, elm; +/** + * 要素の親要素に対する位置。offsetTop + * @alias Node.prototype.y + * @return {number} + * @example node.y(); + */ +function X_Node_y(){ + return X_Node_BoxModel_mesure( this, 'offsetTop' ); - if( !this.parent ){ - console.log( 'xnode.offset() : no parent' ); - return { x : 0, y : 0 }; - }; - Node._body._updateTimerID && Node._body._startUpdate(); - if( !this._root ){ - console.log( 'xnode.offset() : not belong tree.' ); - return { x : 0, y : 0 }; - }; - if( this._state & X_Node_State.DISPLAY_NONE ) return 0; + // this[ 'css' ]( X_Node_CSS_Unit.px, 'top' ); + // this[ 'css' ]( X_Node_CSS_Unit.px, 'transisitonY' ); + //if( X_UA_DOM.IE4 ) return elm ? elm.style.pixelTop : 0; + //return elm ? elm.offsetTop : 0; +}; + +/** + * 要素の文書内の位置。引数に xnode を与えた場合、 + * @alias Node.prototype.offset + * @return {object} { x: {number}, y : {number} } + * @example node.offset(); + */ +function X_Node_offset( xnode ){ + var flags = this[ '_flags' ], + offset = { x : 0, y : 0 }, + obj, parent, elm; - if( X.Dom.Node._body === this || X.Dom.Node._html === this ){ - return { x : 0, y : 0 }; - }; + if( ( ( flags & X_NodeFlags_IN_TREE ) === 0 ) || ( flags & X_NodeFlags_STYLE_IS_DISPLAY_NONE ) ) return offset; - if( X_UA_DOM.W3C ){ - elm = this._rawObject; - } else - if( X_UA_DOM.IE4 ){ - elm = this._rawObject || this._ie4getRawNode(); - } else { - - }; + if( X_Node_body === this || X_Node_html === this ) return offset; + + X_Node_updateTimerID && X_Node_startUpdate(); + + elm = this[ '_rawObject' ] || X_Node__ie4getRawNode && X_Node__ie4getRawNode( this ); - while( elm && elm !== document.body ){ - x += elm.offsetLeft; - y += elm.offsetTop; - elm = elm.offsetParent || elm.parentNode || elm.parentElement; - }; - return { x : x, y : y }; + return elm ? X_Node_getPosition( elm ) : offset; }; + +// エレメントの座標取得 ~スクロール要素~ +// http://n-yagi.0r2.net/script/2009/07/post_16.html + +//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ +// エレメントの絶対座標を得たい +//------------------------------------------------------------------------------ +// 座標取得 +var X_Node_getPosition = + !X_UA[ 'IE4' ] && document.createElement( 'div' ).getBoundingClientRect ? + ( + document.compatMode === 'CSS1Compat' && !X_UA[ 'Webkit' ] ? function( el ){ + var pos = el.getBoundingClientRect(), + html = document.documentElement; + return { x:(pos.left + html.scrollLeft - html.clientLeft) + , y:(pos.top + html.scrollTop - html.clientTop) }; + } : + function( el ){ + var pos = el.getBoundingClientRect(); + return { x:(pos.left + window.pageXOffset) + , y:(pos.top + window.pageYOffset) }; + } + ) : + X_UA[ 'Opera' ] < 10 ? + function( el ){ + var ex = 0; + var ey = 0; + do + { + ex += el.offsetLeft; + ey += el.offsetTop; + } + while( el = el.offsetParent ); + // + return {x:ex,y:ey}; + } : + function(target) + { + var ex = 0; + var ey = 0; + // + var el = target; + var bd = X_elmBody; + + do + { + ex += el.offsetLeft || 0; + ey += el.offsetTop || 0; + } + while( el = el.offsetParent ); + // 要素内スクロール対応 + el = target; + do + { + ex -= el.scrollLeft || 0; + ey -= el.scrollTop || 0; + el = el.parentNode; + } + while( el!=bd ); + // + return {x:ex,y:ey}; + }; + +//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ +