読者です 読者をやめる 読者になる 読者になる

素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

classList、relList

追記:2 Common infrastructure — HTML Standardを見て少し直した。
HTML5 differences from HTML4を見ながら、HTMLElement#classListと#relListを作った。
戻り値や毎回同じclassListオブジェクトを返すべきなのかどうかが不明なので、好きなようにした。

var InvalidCharacterError = function () { };
InvalidCharacterError.prototype = DOMException.prototype;

var TokenList = function (element, attribute, sensitive) {
  this.__element = element;
  this.__attribute = attribute;
  this.__sensitive = sensitive;
};

TokenList.prototype.has = function (name) {
  var list = this._list;
  var value;
  this._checkSpaceCharacter(name);
  value = ' ' + list.join(' ') + ' ';
  if (!this.__sensitive) value = value.toLowerCase();
  if (!this.__sensitive) name = name.toLowerCase();
  return value.indexOf(' ' + name + ' ') !== -1;
};

TokenList.prototype.add = function (name) {
  var list;
  this._checkSpaceCharacter(name);
  if (!this.has(name)) {
    list = this._list;
    list.push(name);
    this.__element.setAttribute(
      this.__attribute,
      list.join(' ')
    );
  }
};

TokenList.prototype.remove = function (name) {
  var list;
  this._checkSpaceCharacter(name);
  if (this.has(name)) {
    if (!this.__sensitive) name = name.toLowerCase();
    list = this._list.filter(function (str) { return str.toLowerCase() !== name; });
    this.__element.setAttribute(this.__attribute, list.join(' '));
  }
};

TokenList.prototype.toggle = function (name) {
  this._checkSpaceCharacter(name);
  if (this.has(name)) this.remove(name);
  else this.add(name);
};

TokenList.prototype.item = function (n) {
  var list = this._list;
  return n in list ? list[n] : null;
};

TokenList.prototype.__defineGetter__(
  'length',
  function () { return this._list.length; }
);

TokenList.prototype.toString = function () {
  return this.__element.getAttribute(this.__attribute) || '';
};

TokenList.prototype._checkSpaceCharacter = function (str) {
  if (/\s/.test(str)) throw new InvalidCharacterError();
};

TokenList.prototype.__defineGetter__(
  '_list',
  function () {
    return (this.__element.getAttribute(this.__attribute) || '').split(/\s+/).sort();
  }
);

(function () {
  var define = function (targets, name, attribute, sensitive) {
    targets.forEach(
      function (target) {
        target.prototype.__defineGetter__(
          name,
          function () { return this.__tokenlist || (this.__tokenlist = new TokenList(this, attribute, sensitive)); }
        );
      }
    );
  };
  define([HTMLElement], 'classList', 'class', true);
  define([HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement], 'relList', 'rel', false);
})();

console.log(document.body.className); // ""
document.body.classList.add('foo');
console.log(document.body.className); // "foo"
document.body.classList.toggle('foo');
console.log(document.body.className); // ""
document.body.classList.remove('xxx');
console.log(document.body.className); // ""

// document.links[0].relList等も同様に動く