Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine Document Order from Nodes

If I have two nodes in an HTML document, how can I tell which one comes first in HTML document order in Javascript using DOM methods?

For example,

function funstuff(a, b) {
    //a and b can be any node in the DOM (text, element, etc)
    if(b comes before a in document order) {
        var t = b; b = a; a = t;
    }
    // process the nodes between a and b. I can handle this part 
    // when I know that a comes before b.
}
like image 952
Michael Avatar asked Aug 17 '09 22:08

Michael


3 Answers

Resig to the rescue:

// Compare Position - MIT Licensed, John Resig
function comparePosition(a, b){
  return a.compareDocumentPosition ?
    a.compareDocumentPosition(b) :
    a.contains ?
      (a != b && a.contains(b) && 16) +
        (a != b && b.contains(a) && 8) +
        (a.sourceIndex >= 0 && b.sourceIndex >= 0 ?
          (a.sourceIndex < b.sourceIndex && 4) +
            (a.sourceIndex > b.sourceIndex && 2) :
          1) +
      0 :
      0;
}
like image 57
Crescent Fresh Avatar answered Nov 02 '22 23:11

Crescent Fresh


You can use the DOM function compareDocumentPosition which will return different numbers based on the two nodes' relationships:

DOCUMENT_POSITION_DISCONNECTED = 0x01;
DOCUMENT_POSITION_PRECEDING = 0x02;
DOCUMENT_POSITION_FOLLOWING = 0x04;
DOCUMENT_POSITION_CONTAINS = 0x08;
DOCUMENT_POSITION_CONTAINED_BY = 0x10;

Potentially the result could be the sum of more than one of these codes as the answer is a bitmask, but I can't imagine a situation where two of these conditions would be true at the same time. Also note that the "disconnected" result would be returned for instance with nodes that have been created but not added to the document tree yet

like image 32
Gareth Avatar answered Nov 03 '22 00:11

Gareth


Rather difficult, I personally would itterate up each tree till I found a common ansester, then check which parent node(or the actual node if that low) comes first starting with firstChild and working through siblings, something like:

  function OrderCheck(node1, node2){

     var ar1 = [null, node1];
     var ar2 = [null, node2];

     for(var i = 1; ar1[i] != null; i++)
       ar1[i+1]=ar1[i].parentNode;
     for(var i = 1; ar2[i] != null; i++)
       ar2[i+1]=ar2[i].parentNode;
     ar1.reverse(); ar2.reverse(); // easier to work with.
     i = 0;
     while( ar1[i] === ar2[i] ){
       if(ar1[i] === null)
         return 0;
       else
         i++
     }

     if(ar1[i] === null)
       return 2;
     if(ar2[i] === null)
       return 1;

     if(i != 0){
       var n = ar1[i-1].firstChild;
       do{
         if(n === ar1[i])
           return 1;
         if(n === ar2[i])
           return 2;
       }while(n = n.nextSibling);
     }
        return -1;// Shouldn't happen.
  }

  var order = OrderCheck(document.body, document.body.previousSibling);
  if( order == 1){
         // element 1 first
  }else if(order == 2){
         // element 2 first
  }else{
         // there was an error.
  }

I did just edit this code in an attempt to fix two possible problems, I haven't tested this new edit however, so if something breaks I shall have to try again. (Edited again to fix a "doesn't even run" style bug).

like image 27
scragar Avatar answered Nov 02 '22 23:11

scragar