Elementオブジェクト(に対する拡張)(3)

makePositionedメソッド

【抜粋】
  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
  },

引数の要素(ID指定可)に適用されているスタイル'position'が'static'もしくは指定なしの場合に、style.positionを'relative'に変更するメソッド。top,leftの指定はしていないので、表示位置は変わりません。

'relative'に変更する、ということよりも、_madePositionedプロパティを作成してtrueに設定するという点が重要なようです。これは次のundoPositionedメソッドで使用します。makePositionedメソッドを使用後、要素位置を移動し、次のundoPositionedメソッドで元に戻すという使い方が想定されます。このため、'positon'が'static'か指定なしの要素のみが対象となります。*1

その後、Opera対応のコードが入っています。コメントをそのまま訳すと、

【訳】
Operaでは、要素の'position'が'relative'で、'top'と'left'が定義されていないとき、
基準要素からの相対位置を返却する。

となります。これは前述getStyleメソッドでも問題になっていました。返却する、というのはgetComputedStyleメソッド*2による取得についてでしょう。style.topとstyle.leftを0にすることで対応しています。他のブラウザに動きを合わせるためだと思われます。

例は次のundoPositionedメソッドにまとめます。

undoPositionedメソッド

【抜粋】
  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
  },

引数の要素(ID指定可)について、_madePositionedプロパティがtrueの場合に、style.position、top、left、bottom、rightを指定なしにするメソッド。直前のmakePositionedメソッド呼出し後に移動された要素を元の位置に戻すためメソッドです。要素のスタイル'position'のデフォルト値が'static'か指定なしでないと機能しません。

【例】
<html>
<head>
<title></title>
<style>
<!--
table, tr, td{
  border:solid blue 2px;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function move(){
  Element.makePositioned('test');
  Element.setStyle('test', {top:'100px', left:'100px'});
}
function undo(){
  Element.undoPositioned('test');
}
//-->
</script>
</head>
<body>
<table><tr><td>AAA</td><td id="test">BBB</td></tr></table>
<button onclick="move();">MOVE</button>
<button onclick="undo();">UNDO</button>
</body>
</html>

id='test'のセルにstyle='position:relative;'を指定すると、UNDOが機能しなくなります。

makeClippingメソッド

【抜粋】
  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element._overflow = element.style.overflow;
    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
      element.style.overflow = 'hidden';
  },

引数の要素(ID指定可)のstyle.overflowを'hidden'にするメソッド。対象要素にはスタイルwidthかheightを指定しないと機能しません。次のundoClippingメソッドと併用することで、要素の内容を一部または全部隠す<->戻す、という処理ができる・・・はずなのですが。ちょっと(というか、かなり)問題があります。

おそらく、すでにこのメソッドで処理済の場合に処理しないという意味で「if (element._overflow) return;」と書かれているのだと思いますが、これは正しくありません。次の「element._overflow = element.style.overflow;」で設定するわけですが、element.style.overflowが指定なしでnullだと、次回実行時も「if (element._overflow) return;」を通過します。そして、デフォルト値を保持するはずのelement._overflowに'hidden'が入ってしまいます。こうなると次のundoClippingメソッドで戻せなくなってしまいます。

次のundoClippingメソッドにも問題があります。後述します。

(2006/07/17追記。どうやらscript.aculo.usのEffectオブジェクト用な気がします。)

例は次のundoClippingメソッドにまとめます。また、改修案も後述します。

undoClippingメソッド

【抜粋】
  undoClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element.style.overflow = element._overflow;
    element._overflow = undefined;
  }

引数の要素(ID指定可)のstyle.overflowを、直前のmakeClippingメソッドで保持した内容に設定するメソッド。ただし、問題があります。とりあえず例を示します。IE6のみでの例です。

【例】メソッド既存のまま。CLIPを2回押すとUNDOできない。
<html>
<head>
<title></title>
<style>
<!--
div{
  border:solid blue 2px;
}
#test{
  width:50px;
  height:20px;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
</head>
<body>
<div id="test">
TESTTEST<br/>
TESTTEST<br/>
TESTTEST<br/>
TESTTEST
</div>
<button onclick="Element.makeClipping('test');">CLIP</button>
<button onclick="Element.undoClipping('test');">UNDO</button>
</body>
</html>

CLIPを押すと要素が縦20px、横50pxに切り出されます。UNDOを押すと元に戻ります。ただし、CLIPを2回押すとUNDOが効かなくなるのは前述したとおりです。

更に、id='test'の要素にstyle='overflow:visible;'を指定してもUNDOが効かなくなります。これは、処理判定「if (element._overflow) return;」に問題があるためです。詳しくは処理を追ってみてください(説明放棄^^;)。

加えて、Netscape等ではoverflow:visible;(または指定なし)でwidth、heightを指定すると、内容に合わせて要素は拡大しません。なので、上の例は、ただ要素のはみ出しが見えるか見えないかを切り替えるだけとなります。

http://www.imgsrc.co.jp/~kuriyama/prototype/prototype.js.html#Element」でも説明放棄されていて、一体何の目的で作成されたのか分からないメソッドです^^; 正式リリースでない最新ver1.5.0_rc0でもこのまま放置されています。

(2006/07/17追記。どうやらscript.aculo.usのEffectオブジェクト用な気がします。)

以下、私が勝手に機能要件を考えて作成した改修案です。要素を指定サイズに切り取る、元に戻すという動作をします。

【改修案】
  makeClipping: function(element, size) {
    element = $(element);
    if(element._makeClipping) return;
    var overflow = Element.getStyle(element, 'overflow');
    if ((overflow || 'visible') != 'hidden'){
      element._makeClipping = true;
      element._overflow = overflow;
      element._width = Element.getStyle(element, 'width');
      element._height = Element.getStyle(element, 'height');
      Element.setStyle(element, size);
      Element.makePositioned(element);
      element.style.overflow = 'hidden';
    }
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._makeClipping) return;
    element.style.width = element._width;
    element.style.height = element._height;
    element.style.overflow = element._overflow;
    Element.undoPositioned(element);
    element._makeClipping = 
     element._width =
     element._height =
     element._overflow = undefined;
  }

makeClippingメソッドの第二引数sizeは{width:'**px', height:'**px'}形式のオブジェクトです。このサイズに要素を切り取ります。undoClippingメソッドで元のサイズに戻します。Element.makePositioned、Element.undoPositionedを使用しているのは、HTMLモードがstandards modeの場合に、要素の内容がはみ出るのを防ぐためです。standards modeの場合、親要素がoverflow:hidden;でpostion:static;、子要素がpostion:relative;だと、子要素がはみ出る場合があります。親要素もpostion:relative;にすると解消します。

【参考】改修案を用いた場合の例
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title></title>
<style>
<!--
#test{
  border:solid blue 2px;
  width:200px;
  height:100px;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
//【改修案による再定義】
Element.makeClipping = function(element, size) {
  element = $(element);
  if(element._makeClipping) return;
  var overflow = Element.getStyle(element, 'overflow');
  if ((overflow || 'visible') != 'hidden'){
    element._makeClipping = true;
    element._overflow = overflow;
    element._width = Element.getStyle(element, 'width');
    element._height = Element.getStyle(element, 'height');
    Element.setStyle(element, size);
    Element.makePositioned(element);
    element.style.overflow = 'hidden';
  }
};

Element.undoClipping = function(element) {
  element = $(element);
  if (!element._makeClipping) return;
  element.style.width = element._width;
  element.style.height = element._height;
  element.style.overflow = element._overflow;
  Element.undoPositioned(element);
  element._makeClipping = 
   element._width =
   element._height =
   element._overflow = undefined;
};
//-->
</script>
</head>
<body>
<div id="test" style='overflow:visible;'>
<div style="position:relative;">
TESTTEST<br/>
TESTTEST<br/>
TESTTEST<br/>
TESTTEST
</div>
<div>AAAAAAA</div>
</div>
<button onclick="Element.makeClipping('test', {width:'50px', height:'50px'});">
CLIP
</button>
<button onclick="Element.undoClipping('test')">UNDO</button>
</body>
</html>

!DCTYPEタグによりHTMLモードはstandards modeとなっています。Element.makePositionedとElement.undoPositionedをコメントアウトすると、CLIP時にstyle="position:relative;"となっている要素がはみ出ます。

*1:もちろん、staticならrelativeにする(戻す)という使い方もあります。後述undoClippingメソッドの改修案を参照してください

*2:document.defaultView.getComputedStyleメソッド->getPropertyValueメソッド