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

【抜粋】一部省略
if (!window.Element) {
  var Element = new Object();
}

Object.extend(Element, {
  visible: function(element) {
    return $(element).style.display != 'none';
  },
(省略)
  undoClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element.style.overflow = element._overflow;
    element._overflow = undefined;
  }
});

要素を操作するためのメソッドを実装します。prototypeプロパティへの追加ではないので、すべてElement.method(parameters)のように使用します。

Elementが既存であれば、それを拡張します。既存でなければ新規にオブジェクトを作成しています。IEではElementクラス・オブジェクトは存在しませんが、NetscapeFirefoxOperaでは存在するようです。

【参考】Elementクラス・オブジェクトのメンバを書き出した結果
(メンバ名):(値)の形式で書き出してみました。

IE6 (Elementなし)
Netscape7.1 prototype:[xpconnect wrapped native prototype]
Firefox1.5.0.4 QueryInterface:function QueryInterface() { [native code] }
Opera9.00 prototype:[object Object]

更に、Operaでprototypeのメンバを書き出すと以下のようになりました。(Netscapeではprototypeメンバの書き出しは不許可のようです)

【参考】OperaのElement.prototypeのメンバ(すべてメソッド)
removeAttributeNode, contains, scrollIntoView, getAttribute, getAttributeNS, 
hasAttribute, hasAttributeNS, setAttribute, setAttributeNS, removeAttribute, 
removeAttributeNS, getAttributeNode, getAttributeNodeNS, setAttributeNode, 
setAttributeNodeNS, getElementsByTagName, getElementsByTagNameNS, dispatchEvent, 
insertBefore, replaceChild, removeChild, appendChild, hasChildNodes, 
cloneNode, normalize, isSupported, hasAttributes, getFeature, addEventListener, 
removeEventListener, attachEvent, detachEvent, lookupPrefix, isDefaultNamespace,
lookupNamespaceURI,

visibleメソッド

【抜粋】
  visible: function(element) {
    return $(element).style.display != 'none';
  },

引数の要素(ID指定可)のスタイルdisplayが'none'でない場合trueを返却、'none'であればfalseを返却します。例は省略。

toggleメソッド

【抜粋】
  toggle: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      Element[Element.visible(element) ? 'hide' : 'show'](element);
    }
  },

引数の要素(ID指定可)すべてについて、現在のスタイルdisplayの値を、'none'と''(指定なし)をひっくり返すメソッドです。前述visibleメソッドの結果によって、後述hideメソッドとshowメソッドを使い分けています。「Element.hide(element)」と「Element['hide'](element)」は同義です。

【例】
<html>
<head>
<title></title>
<style>
div{
  background-color:navy;
  color:white;
  margin:5px;
}
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
</head>
<body>
<div id="test1">test1</div>
<div id="test2">test2</div>
<div id="test3">test3</div>
<div id="test4">test4</div>
<button onclick="Element.toggle('test1');">test1</button>
<button onclick="Element.toggle('test2');">test2</button>
<button onclick="Element.toggle('test3');">test3</button>
<button onclick="Element.toggle('test4');">test4</button>
<button onclick="Element.toggle('test1', 'test2', 'test3', 'test4');">ALL</button>
</body>
</html>

hideメソッド

【抜粋】
  hide: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      element.style.display = 'none';
    }
  },

引数の要素(ID指定可)すべてについて、スタイルdisplayの値を'none'にします。例は省略。

showメソッド

【抜粋】
  show: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      element.style.display = '';
    }
  },

引数の要素(ID指定可)すべてについて、スタイルdisplayの値を''(空文字列)にします。この場合、要素のデフォルトの設定が適用されます(divならblock、spanならinline)。ただし、class等で指定があればそちらが適用されます。

例は省略します。

removeメソッド

【抜粋】
  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
  },

引数の要素(ID指定可)を削除するメソッド。parentNodeは要素の親要素が格納されているパラメータで、removeChildメソッドは子要素を削除するメソッド。どちらも既存です。削除なのでhide->showのように復活させることはできません。

例は省略します。

updateメソッド

【抜粋】
  update: function(element, html) {
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
  },

引数elementの要素(ID指定可)に引数htmlを流し込むメソッド。ただし、html内にスクリプトタグがある場合はこれを0.01秒後に実行しています。innerHTMLで流し込まれたスクリプトタグは無効となるため、この処置をしているものと思われます。0.01秒後というのは、描画が終わるのを待つためだと思います。ですから、大量に描画させたりすると、スクリプトがエラーとなる場合も想定されます(扱う要素がまだ存在しないため)。*1

【例】
<html>
<head>
<title></title>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script language="javascript">
<!--
function test(id){
  var str = "<div id='test2'>test2</div>"
          + "<script>$('test2').style.backgroundColor='yellow';</script>";
  Element.update(id, str);
}
//-->
</script>
</head>
<body>
<div id="test1">test1</div>
<button onclick="test('test1');">TEST/button>
</body>
</html>

TESTボタンを押すとDIV要素'test1'の内容を書き換え、その後スクリプトでDIV要素'test2'の背景色を黄色にしています。このとき、もし'test2'がまだ描画されていなければエラーになるわけです。*2

getHeightメソッド

【抜粋】
  getHeight: function(element) {
    element = $(element);
    return element.offsetHeight;
  },

引数の要素(ID指定可)の画面上の高さをpxで取得するメソッド。要素のoffsetHeightプロパティには常に現状の値が入りますので、これを返却します。style.height等と違って単位(px)がつかない、数値です。例は省略します。

classNamesメソッド

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

引数の要素(ID指定可)を引数として、後述Element.ClassNamesクラスのインスタンス作成し、返却するメソッド。Element.ClassNamesクラスは要素のCSSクラス名を扱うクラスです。

例は後述removeClassNameメソッドにまとめます。

hasClassNameメソッド

【例】
  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).include(className);
  },

引数elementの要素(ID指定可)に指定されているCSSクラス名に、引数classNameが存在するかをチェックするメソッド。存在すればtrue、しなければfalseを返却します。

該当要素が存在しない場合は返却値なしです。判定の際にはundefinedかどうかで確かめることができます。

Element.classNamesクラスはEnumerableクラスを継承していますので、includeメソッドを実装しています。処理対象は要素の各CSSクラス名です。

例は後述removeClassNameメソッドにまとめます。

addClassNameメソッド

【抜粋】
  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).add(className);
  },

引数の要素(ID指定可)のCSSクラスに、引数classNameを追加するメソッド。該当要素が存在しない場合に返却値なしなのはhasClassNameメソッドと同じです。

実際の処理はElement.classNamesクラスのaddメソッドで行います。

例は後述removeClassNameメソッドにまとめます。

removeClassNameメソッド

【抜粋】
  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).remove(className);
  },

引数の要素(ID指定可)のCSSクラスから、引数classNameを削除するメソッド。該当要素が存在しない場合に返却値なしなのは前二つのメソッドと同じ。

実際の処理はElement.classNamesクラスのremoveメソッドで行います。

【例】
<html>
<head>
<title></title>
<style>
<!--
.classA{
  border:solid blue 2px;
}
.classB{
  background-color:yellow;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function showClass(){
  $("test1").innerHTML = Object.inspect(Element.classNames("test2")).escapeHTML();
}
//-->
</script>
</head>
<body onload="showClass();">
<div id="test1"></div>
<div id="test2" class="classA">TEST</div>
<button onclick="alert(Element.hasClassName('test2', 'classB'));showClass();">
hasClassName classB?
</button>
<button onclick="Element.addClassName('test2', 'classB');showClass();">
addClassName classB
</button>
<button onclick="Element.removeClassName('test2', 'classB');showClass();">
removeClassName classB
</button>
</body>
</html>

Element.classNamesメソッドはshowClass関数内で使用しています。作成したインスタンスをObject.inspectで文字列化し、escapeHTMLメソッド*3エスケープしてから表示しています。Element.classNamesクラスではinspectメソッドをオーバーライドしていないので、Enumerableクラスのinspectメソッドが使われます。

cleanWhitespaceメソッド

【抜粋】
  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    for (var i = 0; i < element.childNodes.length; i++) {
      var node = element.childNodes[i];
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        Element.remove(node);
    }
  },

引数の要素(ID指定可)の子ノードのうち、空白のみ、もしくは空文字のテキストノードを削除するメソッド・・・ってまんま書いてありますが^^; 要素内の空白文字を消す、ではないのでお間違えなきよう。

nodeTypeの3はテキストノードを意味します。詳しくは以下を参照してください。

【参考サイト】
http://allabout.co.jp/internet/javascript/closeup/CU20040307/index.htm

正規表現の「\S」は「空白以外の文字」を意味します。「\s」の「空白文字」とは逆です。*4

なんでこんなメソッドがあるのかよく分からなかったのですが、テストしているうちにちょっと分かった気がします。とりあえず、例を提示します。

【例】
<html>
<head>
<title></title>
<style>
<!--
span{
  font-weight:bold;
  color:white;
}
#parent{
  background-color:red;
}
#child1{
  background-color:green;
}
#child2{
  background-color:blue;
}
#child3{
  background-color:gray;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function showLength(){
  $('test').innerHTML=$('parent').childNodes.length;
}
//-->
</script>
</head>
<body onload="showLength();">

<div id="test"></div>

<span id="parent">
<span id="child1">CHILD1</span>
<span id="child2">CHILD2</span>
<span id="child3">CHILD3</span>
</span>
	
<button onclick="Element.cleanWhitespace('parent');showLength();">
TEST
</button>

</body>
</html>

表示直後は最初にIEなら「6」、Netscape等は「7」と表示されます。これが要素"parent"の子ノードの数です。その後、TESTボタンを押すとこれが「3」になって、ちょっと表示が変わります。赤が見えなくなると思います。

"parent"以下には"child1"、"child2"、"child3"の3つしか要素がないはずなのに、最初はなんでそれ以上あるのでしょう?

これは、spanタグの書き方に原因があります。以下のように変更してみます。

<span id="parent"><span id="child1">CHILD1</span><span id="child2">CHILD2</span><span id="child3">CHILD3</span></span>

横に長くなってすみませんが・・・。こうすると、最初から子ノードの数が「3」になり、赤色も見えないと思います。

つまり、各子要素のspanタグ後の改行が原因です。改行だけでなく、空白やタブでも同様です。タグとタグの間に、文字はなくても、改行・空白・タブがあると、空白のみのテキストノードが作成されてしまうようです。これはspanタグ等のinline要素では表示に影響します。divタグ等のblock要素では表示に影響しないようですが、空白のみのテキストノードは作成されます。

IENetscape等の数の違いは、親要素span開始タグ直後の改行を、対象とするかしないかの違いのようです。

なので、こういう現象を解消するためのメソッドではないかと思うのですが。。。

余談ですが、この現象を回避した上で、コードが横に長くなるのを防ぐために、以下の記法をしているのを見たことがあります。

<span id="parent"
><span id="child1">CHILD1</span
><span id="child2">CHILD2</span
><span id="child3">CHILD3</span
></span>

あまりみかけないと思うのですが、一般的なのでしょうか?→一般的みたいですね。

emptyメソッド

【抜粋】
  empty: function(element) {
    return $(element).innerHTML.match(/^\s*$/);
  },

引数の要素(ID指定可)のソース(innerHTML)が、空白(改行・タブ含む)のみ、もしくは空文字であればtrueを返却します。そうでなければfalseを返却します。

例は省略します。

scrollToメソッド

【抜粋】
  scrollTo: function(element) {
    element = $(element);
    var x = element.x ? element.x : element.offsetLeft,
        y = element.y ? element.y : element.offsetTop;
    window.scrollTo(x, y);
  },

引数の要素(ID指定可)の位置へウィンドウをスクロールさせるメソッド。(アニメーションはしません)

要素のoffsetLeftとoffsetTopプロパティについては以下を参照してください。

offsetTop/offsetLeft/offsetParentの闇 - Backstage of theater.js

ここを読んでもらうと分かると思うのですが、このメソッドは場合によって不具合を起こします。具体的には、引数の要素のoffsetParentが、body要素(もしくはhtml要素)でない場合です。ここは以下のようにしたほうがベターな気がします(完全とはいえない。Position.cumulativeOffsetにもちょっと問題があるため)。

【改修案】
  scrollTo: function(element) {
    element = $(element);
    var offset = Position.cumulativeOffset(element);
    var x = element.x ? element.x : offset[0],
        y = element.y ? element.y : offset[1];
    window.scrollTo(x, y);
  },

プロパティのx、yって、もしかして、イベントオブジェクトのプロパティのことかな・・・。イベントオブジェクトを引数にすれば、クリックしたところへスクロールすることができますね。

window.scrollToはウィンドウのスクロール位置を変える既存メソッドです。

【例】
<html>
<head>
<title></title>
<style>
<!--
div{
  border:solid blue 2px;	
}
#test2{
  position:absolute;
  width:200px;
  top:10px;
  left:1000px;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
</head>
<body>
<button onclick="Element.scrollTo('test1');">下へ</button>
<button onclick="Element.scrollTo('test2');">横へ</button>
<div style="height:1000px;"></div>
<div id="test1">下</div>
<div style="height:1000px;"></div>
<div id="test2">横</div>
</body>
</html>

>>Elementオブジェクト(に対する拡張)(2)につづく<<

*1:stripScriptsとevalScriptsは、Stringクラスへの拡張で追加されたメソッド。

*2:Operaではスクリプト部分がエラーになります。Stringクラス拡張メソッドevalScriptsの不具合です。v1.5.0_rc0では改善されています。

*3:Stringクラス拡張メソッド

*4:「\s」が表すのは半角空白のほか、タブ、改行等も含まれます。全角空白はブラウザによって扱いが違いますので注意してください。