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

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

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

ルビをつける

WebKitrubyタグのレンダリングのサポートが入ったので、新聞社のサイトなどで狡猾(こうかつ)な犯行などと書いてあるものをrubyタグで包むスクリプトを書いた。

// console.time("benchmark");
var r = document.evaluate(
  './/text()[contains(., "(")]',
  document,
  null,
  XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  null
);

var i;
var node;
var re = /(.*?)([一-龠]+)(([ーぁ-んァ-ヶ]+))/g;
var match;
var range = document.createRange();
var text;
for (i = 0; i < r.snapshotLength; i++) {
  node = r.snapshotItem(i);
  text = node.textContent;
  if (!re.test(text)) {
    continue;
  }
  range.selectNode(node);
  node.parentNode.replaceChild(
    range.createContextualFragment(
      text.replace(re, "$1<ruby>$2<rp>(</rp><rt>$3</rt><rp>)</rp></ruby>")
    ),
    node
  );
}
range.detach();
// console.timeEnd("benchmark");

createContextualFragmentやinnerHTMLを使うとこのようにシンプルに書けるが大量にマッチする場合速度が少し落ちる。
で、これを使わないと見ての通り急に複雑になる。ただし、速度は少し上がる。

// console.time("benchmark");
var r = document.evaluate(
  './/text()[contains(., "(")]',
  document,
  null,
  XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  null
);

var i;
var node;
var re = /([一-龠]+)(([ーぁ-んァ-ヶ]+))/g;
var match;
var left;
var fragment;
var text;
for (i = 0; i < r.snapshotLength; i++) {
  node = r.snapshotItem(i);
  text = node.textContent;
  fragment = document.createDocumentFragment();
  if (!re.test(text)) {
    continue;
  }
  re.lastIndex = 0;
  left = 0;
  while ((match = re.exec(text))) {
    fragment.appendChild(
      document.createTextNode(text.slice(left, re.lastIndex - match[0].length))
    );
    fragment.appendChild(createRuby(match[1], match[2]));
    left = re.lastIndex;
  }
  fragment.appendChild(
    document.createTextNode(text.slice(left))
  );
  node.parentNode.replaceChild(fragment, node);
}

function createRuby(text, annotation) {
  var ruby = document.createElement("ruby");
  var rp1 = document.createElement("rp");
  var rt = document.createElement("rt");
  var rp2 = document.createElement("rp");
  rp1.appendChild(document.createTextNode("("));
  rt.appendChild(document.createTextNode(annotation));
  rp2.appendChild(document.createTextNode("("));
  ruby.appendChild(document.createTextNode(text));
  ruby.appendChild(rp1);
  ruby.appendChild(rt);
  ruby.appendChild(rp2);
  return ruby;
}
// console.timeEnd("benchmark");