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

AutoPagerize#SITEINFOのinsertBeforeを省略する際の精度をあげる

AutoPagerize

AutoPagerize0.0.18 - SWDYHより、

siteinfoにinsertBeforeがなくてもいいようにしました。

とあって、実装は下記のようになっている。

    if (info.insertBefore) {
        this.insertPoint = getFirstElementByXPath(info.insertBefore)
    }
    else {
        var lastPageElement = getElementsByXPath(info.pageElement).pop()
        if (lastPageElement) {
            this.insertPoint = getFirstElementByXPath('following-sibling::node()', lastPageElement)
        }
    }

.insertBeforeが空だったら、{{pageElementの最後の要素}の次に来る兄弟要素}をinsertPointにしている。
改行なりスペースのひとつでもあれば、見た目にはわからなくてもテキストノードが存在することになるので、
汎用的に動きそうに思える。
が、Googleなどhtmlをコンパクトにしている場合、改行もスペースもないのでinsertPointも空になってしまう。
こういった場合は普通ならinsertBeforeの代わりにappendChildすればよいが、今回はそれをすると変更箇所が多くなるので避けたいところ。
そこで、スペースだけのテキストノードをappendChildしておいて、その要素をinsertPointにする案を考えた。
ので、oAutoPagerizeで次のように実装してみた。

  var lastPageElement = getElementsByXPath(info.pageElement).pop();
  this.insertPoint = lastPageElement.nextSibling || 
     lastPageElement.parentNode.appendChild(document.createTextNode(' '));

nextSiblingが空だったらparentNodeにTextNodeをappendChildしていて、ちょうどappendChildがTextNodeを返してくれているので、insertPointにはそのTextNodeが入ってくれる。

ちなみに、

getFirstElementByXPath('following-sibling::node()', lastPageElement)

は、「following-sibling 基準点はコンテキストノードの後ろにあるすべての兄弟ノードを選択」し、「::node() はノード型に関係なく、コンテキストノードのすべての子ノードを選択する」したものから最初の要素を取得している*1
これは自分の兄弟であり、直後に当たる要素なので、

lastPageElement.nextSibling

つまりはnextSiblingになる。(調べた限り間違いないと思うんだけど、やっぱり言い切るのは不安だなぁ。。)
なので、DOMで一発で取れる場合はXPathのほうが効率悪いので、nextSiblingを使っている。

*1:http://www.infoteria.com/jp/contents/xml-data/REC-xpath-19991116-jpn.htm