2011-11-30 20 views
6

DOM4 compareDocumentPositionprzeglądarka porównuje pozycję dokumentu

Chcę zaimplementować polecenie compareDocumentPosition. Resig dokonał great start at doing just this. Wziąłem swój kod i neatened go

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; 
} 

Działa to dla Element ale nie robi dla Text lub DocumentFragment. Dzieje się tak, ponieważ IE8 nie daje .sourceIndex na tych węzłach. (Nie daje .contains albo ale Naprawiłem już ten problem)

Jak skutecznie pisać bitu +=4 i +=2 które odpowiadają DOCUMENT_POSITION_FOLLOWING i DOCUMENT_POSITION_PRECEDING.

Dla dodatkowego odniesienia te dwa są określone przez drzewo rzędu które DOM4 określa jako

Przedmiotem A poprzedzające obiektu B, gdy A i B są w tym samym drzewa i jest przed B w kolejności drzewa .

Obiekt A podąża za obiektem B, jeśli A i B znajdują się w tym samym drzewie, a A występuje po B w kolejności drzewa.

Kolejność drzew jest w przedsprzedaży, pierwsze przemieszczenie na głębokość.

Większość nowoczesnych przeglądarek implementuje to (w tym IE9). Więc trzeba tylko coś, co działa w IE8 (nie dbam o IE6/7, ale czy to działa super!)

Odpowiedz

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; 
    } 
} 

Napisałem go samodzielnie. Myślałem, że ta implementacja była błędna, ale był to błąd w innym moim kodzie. Wydaje się całkiem solidny.

+5

Oh Raynos, to niesamowite . Dziękuję za odpowiedź na moje pytanie. – Raynos

+0

Wszystko w porządku ...? –

+0

@TimDown Nie spałem wystarczająco. – Raynos

0

Odpowiedź od Raynosa to najlepszy początek, ale nie można go uruchomić po wyjęciu z pudełka. Node.* nie można znaleźć, a .bind nie jest dostępny w IE8.

Oto kod gotowy do użycia w programie 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; 
    } 
} 

to nazwać tak:

compareDocumentPosition(sourceElement, elementToTest) 

(To tak, jakby wzywając sourceElement.compareDocumentPosition(elementToTest))