HTML 要素にクラスを付与したり削除したりするのに便利なユーティリティメソッド (JavaScript)

HTML5 における HTMLElement#classList

HTML5 では、HTML DOM の HTMLElement インターフェイスclassList というプロパティ が追加される予定です。 このプロパティは W3C DOM 4 の DOMTokenList インターフェイス を実装したオブジェクトを参照しており、このリストに値を追加したり、このリストから値を削除すると、要素のクラス属性に反映されます。

つまり、HTMLElement#classList を使うことで、要素にクラスを追加したり削除したりすることが簡単にできます。 詳細は MDN あたりが分かりやすいと思います。

しかしながら、現在のところ HTMLElement#classList が使える環境は多くはありません。 Firefox 7 や Opera 11.51 では使えることを確認しました (Google Chrome でも使えるようです) が、Safari 5 や IE 10 では実装されていません。

ユーティリティメソッドを書いてみた

そんなわけで classList が使える環境でも使えない環境でもクラスの追加や削除をしやすいようにユーティリティメソッドを書いてみました。

var ClassListUtil = {};
(function ns() {
var toUnicodeEscapedString = function toUnicodeEscapedString( str ) {
    // ECMAScript は UTF-16 ベースなのでサロゲートペアの扱いもこれで良い
    var strt = [];
    var i, len, j;
    for( i = 0, len = str.length; i < len; ++ i ) {
        var s = "\\u";
        var n = str.charCodeAt( i ).toString( 16 );
        for( j = 4 - n.length; 0 < j; -- j ) s += "0";
        s += n;
        strt.push( s );
    }
    return strt.join( "" );
};
ClassListUtil.toUnicodeEscapedString = toUnicodeEscapedString;
var createRegExp = function createRegExp( className ) {
    return new RegExp( "(?:^|[\x09\x0A\x0C\x0D\x20])" 
            + toUnicodeEscapedString( className ) + "(?=$|[\x09\x0A\x0C\x0D\x20])", "g" );
};
ClassListUtil.createRegExp = createRegExp;
var add = function add( elem, className ) {
    var list = elem.classList;
    if( list && list.add ) {
        list.add( className );
    } else {
        this._add( elem, createRegExp( className ), className );
    }
};
ClassListUtil.add = add;
var remove = function remove( elem, className ) {
    var list = elem.classList;
    if( list && list.remove ) {
        list.remove( className );
    } else {
        this._remove( elem, createRegExp( className ) );
    }
};
ClassListUtil.remove = remove;
var contains = function contains( elem, className ) {
    var list = elem.classList;
    if( list && list.contains ) {
        return list.contains( className );
    } else {
        return _contains( elem, createRegExp( className ) );
    }
};
ClassListUtil.contains = contains;
var toggle = function toggle( elem, className ) {
    var list = elem.classList;
    if( list && list.toggle ) {
        return list.toggle( className );
    } else {
        return _toggle( elem, createRegExp( className ), className );
    }
};
ClassListUtil.toggle = toggle;
var _add = function _add( elem, regexp, className ) {
    var targetClassListStr = elem.className;
    if( ! regexp.test( targetClassListStr ) ) {
        elem.className = targetClassListStr + " " + className;
    }
};
ClassListUtil._add = _add;
var _remove = function _remove( elem, regexp ) {
    var targetClassListStr = elem.className;
    elem.className = elem.className.replace( regexp, "" );
};
ClassListUtil._remove = _remove;
var _contains = function _contain( elem, regexp ) {
    var targetClassListStr = elem.className;
    return regexp.test( targetClassListStr );
};
ClassListUtil._contains = _contains;
var _toggle = function _toggle( elem, regexp, className ) {
    var hasClassName = _contains( elem, regexp );
    if( hasClassName ) {
        _remove( elem, regexp );
    } else {
        _add( elem, regexp, className );
    }
    return ! hasClassName;
};
ClassListUtil._toggle = _toggle;
})();

使い方はこんな感じになります。

var elem = document.getElementById( "id-of-element" );
// クラスを追加
ClassListUtil.add( elem, "test-class" );
// クラスを削除
ClassListUtil.remove( elem, "test-class" );
// クラスのトグル 
// (存在していれば削除し, 存在していなければ追加. 前者の場合 false が返り, 後者は true)
ClassListUtil.toggle( elem, "test-class" );
// クラスを持っているかどうか確認
ClassListUtil.contains( elem, "test-class" ); //=> true