Hatena::ブログ(Diary)

Marginal Leaves RSSフィード

2007-04-23

prototype.jsの$$で:containsとその拡張を使えるようにしてみる

こんなセレクタが CSS にあれば便利なのにと思うセレクタ - lucky bag」を読んで、妄想してるだけでもなんなのでprototype.jsの$$で使えるようにして遊んでみました。ちなみに超やっつけですので絶対どっかおかしいと思います。一応IE6,Firefox2ではちょろっと確認しています。

prototype.jsを読み込んだ後に以下のソースを読み込みます。prototype.jsのバージョンは1.5.1_rc3です。1.5.0じゃ動きません。

Selector.patterns.pseudo = /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not|contains)(\((.*?)\))?(\b|$|\s)/;
Selector.pseudos['contains'] = function(nodes, selector, root) {
  nodes = $A(nodes);
  var m;
  if (m = selector.match(/^\s*(["'])(.*)\1\s*$/)) {
    return nodes.select(function(node) {
      var text = [];
      (function collectText(node) {
        var childs = node.childNodes;
        for (var i = 0, il = childs.length; i < il; ++i) {
          if (childs[i].nodeType == 3) {
            text.push(childs[i].nodeValue);
          } else if (childs[i].nodeType == 1) {
            collectText(childs[i]);
          }
        }
      })(node);
      return text.join('').include(m[2]);
    });
  } else {
    var s = new Selector(selector);
    return nodes.select(function(node) {
      return s.findElements(node).length > 0;
    });
  }
};
Selector.xpath.pseudos['contains'] = function(m) {
  var e = m[6], p = Selector.patterns,
      x = Selector.xpath, le, m, v;
  if (m = e.match(/^\s*(["'])(.*)\1\s*$/)) {
    return "[contains(.,'" + m[2] + "')]";
  }
  var exclusion = [];
  while (e && le != e && (/\S/).test(e)) {
    le = e;
    for (var i in p) {
      if (m = e.match(p[i])) {
        v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
        exclusion.push("(" + v.substring(1, v.length - 1) + ")");
        e = e.replace(m[0], '');
        break;
      }
    }
  }
  return "[.//*[" + exclusion.join(" and ") + "]]";
};
Selector.findChildElements = function(element, expressions) {
  var exprs = expressions.join(','), expressions = [];
  exprs.scan(/(([\w#:.~>+\s-]+|\*|\[.*?\]|\(.*?\))+)\s*(,|$)/, function(m) {
    expressions.push(m[1].strip());
  });
  var results = [], h = Selector.handlers;
  for (var i = 0, l = expressions.length, selector; i < l; i++) {
    selector = new Selector(expressions[i].strip());
    h.concat(results, selector.findElements(element));
  }
  return (l > 1) ? h.unique(results) : results;
};

ちなみに、例に出てた

li:contains(img) {
  list-style-type: none;
}

を適用しようと思ったら、たとえば以下のように書かないといけません。

(function(css) {
  Event.observe(window, 'load', function() {
    for (selector in css)
      $$(selector).each(function(e) { e.setStyle(css[selector]); });
  });
})({
  'li:contains(img)':{
    'list-style-type': 'none'
  }
});

:contains引数には任意のセレクタが使えるようにしてあるので、もっと複雑な事も出来ると思います。もちろん:contains("foo")という形も使えます。たぶん。

[2007-04-26 00:04追記] e-luckさんが「セレクタ引数が同じだとループしちゃうのかな」って言っていたのですが、このコードだと例えばli:contains(li)は「li要素を子孫に持つli要素」を指します。そういう意味じゃないのかな? 私の認識がちょっと違ったみたいです。

[2007-04-26 10:05追記] 1.5.1_rc3用にしたかも。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/margin/20070423/1177336656