2011-11-30 12 views
6

DOM4 compareDocumentPositionnavegador cruz comparar documento posición

Quiero implementar compareDocumentPosition. Resig ha realizado un great start at doing just this. Me he tomado su código y neatened arriba

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

Esto funciona para Element pero no para Text o DocumentFragment. Esto es porque IE8 no da .sourceIndex en esos nodos. (No da .contains bien pero me he fijado ese problema ya)

¿Cómo escribo de manera eficiente la +=4 y +=2 bits que corresponden a DOCUMENT_POSITION_FOLLOWING y DOCUMENT_POSITION_PRECEDING.

Para referencia adicional a los dos están definidos por el árbol de orden que DOM4 define como

Un objeto A se precede un objeto B si A y B están en el mismo árbol y A viene antes de B con el fin árbol .

Un objeto A sigue a un objeto B si A y B están en el mismo árbol y A aparece después de B en orden de árbol.

La orden de árbol es preordenar, recorrido transversal en profundidad.

La mayoría de los navegadores modernos implementan esto (incluido IE9). Por lo que sólo necesita algo que funciona en IE8 (no me importa IE6/7, pero si funciona increíble!)

Respuesta

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

lo escribí yo mismo. Pensé que esta implementación tenía errores, pero era un error en algún otro código mío. Parece bastante sólido.

+5

Oh Raynos, que impresionante . Gracias por responder mi pregunta – Raynos

+0

¿Estás bien ...? –

+0

@TimDown No he dormido lo suficiente. – Raynos

0

La respuesta de Raynos es un comienzo superior, pero no se puede ejecutar de la caja. Node.* no se puede encontrar y .bind no está disponible en IE8.

Este es el código listo para su uso en 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; 
    } 
} 

Usted lo llama así:

compareDocumentPosition(sourceElement, elementToTest) 

(Es como llamar sourceElement.compareDocumentPosition(elementToTest))

Cuestiones relacionadas