2011-11-30 83 views
6

DOM4 compareDocumentPosition跨瀏覽器比較文檔位置

我想實現compareDocumentPosition。 Resig已經做出了great start at doing just this。我已經採取了他的代碼,並neatened它

function compareDocumentPosition(other) { 
    var ret = 0; 
    if (this.contains) { 
     if (this !== other && this.contains(other)) { 
      ret += 16; 
     } 
     if (this !== other && other.contains(this)) { 
      ret += 8; 
     } 
     if (this.sourceIndex >= 0 && other.sourceIndex >= 0) { 
      if (this.sourceIndex < other.sourceIndex) { 
       ret += 4; 
      } 
      if (this.sourceIndex > other.sourceIndex) { 
       ret += 2; 
      } 
     } else { 
      ret += 1; 
     } 
    } 
    return ret; 
} 

這適用於Element但並不爲TextDocumentFragment。這是因爲IE8不會在這些節點上提供.sourceIndex。 (它不給.contains要麼但我已經固定了這個問題)

如何有效地寫+=4+=2位對應於DOCUMENT_POSITION_FOLLOWINGDOCUMENT_POSITION_PRECEDING

對於額外的參考這兩個由該DOM4定義爲

如果A和B在相同的樹和A按照樹順序接在B之前的對象的是一種對象B前述樹順序定義。

如果A和B位於同一棵樹中,並且A以樹的順序位於B之後,則對象A位於對象B之後。

樹的順序是先序,深度優先遍歷。

大多數現代瀏覽器都實現這個功能(包括IE9)。所以你只需要一些能在IE8中工作的東西(我不在乎IE6/7,但是如果它真的很棒!)

回答

10
function recursivelyWalk(nodes, cb) { 
    for (var i = 0, len = nodes.length; i < len; i++) { 
     var node = nodes[i]; 
     var ret = cb(node); 
     if (ret) { 
      return ret; 
     } 
     if (node.childNodes && node.childNodes.length) { 
      var ret = recursivelyWalk(node.childNodes, cb); 
      if (ret) { 
       return ret; 
      } 
     } 
    } 
} 

function testNodeForComparePosition(node, other) { 
    if (node === other) { 
     return true; 
    } 
} 

function compareDocumentPosition(other) { 
    function identifyWhichIsFirst(node) { 
     if (node === other) { 
      return "other"; 
     } else if (node === reference) { 
      return "reference"; 
     } 
    } 

    var reference = this, 
     referenceTop = this, 
     otherTop = other; 

    if (this === other) { 
     return 0; 
    } 
    while (referenceTop.parentNode) { 
     referenceTop = referenceTop.parentNode; 
    } 
    while (otherTop.parentNode) { 
     otherTop = otherTop.parentNode; 
    } 

    if (referenceTop !== otherTop) { 
     return Node.DOCUMENT_POSITION_DISCONNECTED; 
    } 

    var children = reference.childNodes; 
    var ret = recursivelyWalk(
     children, 
     testNodeForComparePosition.bind(null, other) 
    ); 
    if (ret) { 
     return Node.DOCUMENT_POSITION_CONTAINED_BY + 
      Node.DOCUMENT_POSITION_FOLLOWING; 
    } 

    var children = other.childNodes; 
    var ret = recursivelyWalk(
     children, 
     testNodeForComparePosition.bind(null, reference) 
    ); 
    if (ret) { 
     return Node.DOCUMENT_POSITION_CONTAINS + 
      Node.DOCUMENT_POSITION_PRECEDING; 
    } 

    var ret = recursivelyWalk(
     [referenceTop], 
     identifyWhichIsFirst 
    ); 
    if (ret === "other") { 
     return Node.DOCUMENT_POSITION_PRECEDING; 
    } else { 
     return Node.DOCUMENT_POSITION_FOLLOWING; 
    } 
} 

我自己寫的。我認爲這個實現有問題,但它是我的其他代碼中的一個錯誤。看起來很穩固。

+5

哦Raynos,你真棒。謝謝你回答我的問題 – Raynos

+0

你還好嗎....? –

+0

@TimDown我睡不夠。 – Raynos

0

Raynos的答案是一個開頭,但不是開箱即用的。找不到Node.*,並且.bind在IE8中不可用。

下面是代碼就可以使用在Internet Explorer 8:

function recursivelyWalk(nodes, cb) { 
    for (var i = 0, len = nodes.length; i < len; i++) { 
     var node = nodes[i]; 
     var ret = cb(node); 
     if (ret) { 
      return ret; 
     } 
     if (node.childNodes && node.childNodes.length) { 
      var ret = recursivelyWalk(node.childNodes, cb); 
      if (ret) { 
       return ret; 
      } 
     } 
    } 
} 

function testNodeForComparePosition(node, other) { 
    if (node === other) { 
     return true; 
    } 
} 

var DOCUMENT_POSITION_DISCONNECTED = 1; 
var DOCUMENT_POSITION_PRECEDING = 2; 
var DOCUMENT_POSITION_FOLLOWING = 4; 
var DOCUMENT_POSITION_CONTAINS = 8; 
var DOCUMENT_POSITION_CONTAINED_BY = 16; 

function compareDocumentPosition(thisNode, other) { 
    function identifyWhichIsFirst(node) { 
     if (node === other) { 
      return "other"; 
     } else if (node === reference) { 
      return "reference"; 
     } 
    } 

    var reference = thisNode, 
     referenceTop = thisNode, 
     otherTop = other; 

    if (this === other) { 
     return 0; 
    } 
    while (referenceTop.parentNode) { 
     referenceTop = referenceTop.parentNode; 
    } 
    while (otherTop.parentNode) { 
     otherTop = otherTop.parentNode; 
    } 

    if (referenceTop !== otherTop) { 
     return DOCUMENT_POSITION_DISCONNECTED; 
    } 

    var children = reference.childNodes; 
    var ret = recursivelyWalk(
     children, 
     function(p) { 
      (function() { 
       var localOther = other; 
       return testNodeForComparePosition(localOther, p); 
      })(); 
     } 
    ); 
    if (ret) { 
     return DOCUMENT_POSITION_CONTAINED_BY + 
      DOCUMENT_POSITION_FOLLOWING; 
    } 

    var children = other.childNodes; 
    var ret = recursivelyWalk(
     children, 
     function(p) { 
      (function() { 
       var localOther = reference; 
       return testNodeForComparePosition(localOther, p); 
      })(); 
     } 
    ); 
    if (ret) { 
     return DOCUMENT_POSITION_CONTAINS + 
      DOCUMENT_POSITION_PRECEDING; 
    } 

    var ret = recursivelyWalk(
     [referenceTop], 
     identifyWhichIsFirst 
    ); 
    if (ret === "other") { 
     return DOCUMENT_POSITION_PRECEDING; 
    } else { 
     return DOCUMENT_POSITION_FOLLOWING; 
    } 
} 

你這樣稱呼它:

compareDocumentPosition(sourceElement, elementToTest) 

(這就像調用sourceElement.compareDocumentPosition(elementToTest)