DOM Traversal の出番
({ observe: function (doc) { this.iterator = doc.createNodeIterator(doc, NodeFilter.SHOW_ELEMENT, this, true); doc.addEventListener('keydown', this, false); }, acceptNode: function (node) { if ('H1 H2 H3 H4 H5 H6'.indexOf(node.tagName) >= 0) { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_SKIP; }, handleEvent: function (evt) { var CTRL = evt.ctrlKey, SHIFT = evt.shiftKey, ALT = evt.altKey, META = evt.metaKey, code = evt.keyCode, node; if (code === 40 && ALT && !CTRL && !SHIFT && !META) { if ((node = this.iterator.nextNode())) { node.scrollIntoView(); evt.preventDefault(); } } else if (code === 38 && ALT && !CTRL && !SHIFT && !META) { if ((node = this.iterator.previousNode())) { node.scrollIntoView(); evt.preventDefault(); } } } }).observe(document);
acceptNode は CSS セレクタから作れるようにして、イベントとアクションの対応を関数内に書かないようにしよう(イベントセレクタ? イベントフィルタ? のようなものを作る)。
メモ:
- Opera 12.02 では「Alt + 修飾キー以外のキー」という組み合わせでは keydown イベントが発生しない。
- Chrome 21 では NodeFilter::acceptNode の this 値が window になっている(今回のコードでは問題にならない)。
// そういえばJavaScriptリファレンス 第6版の索引だけ立ち読みしたら、TreeWalker や NodeIterator が載っていないようだった。IE もバージョン 9 でようやく使えるようになったのに。