Is element before or after another element in DOM

node.compareDocumentPosition

Summary
Compares the position of the current node against another node in any other document.

UPDATE: This does not work in all browsers but there is a fix for that. Thanks for Alnitak (see answer comments) for providing the link: cross browser compare document position


A brute force approach may be to get all elements, then get the index of each element within the set.

var all = $('*');

var a_index = all.index($('#element_a'));
var b_index = all.index($('#element_b'));

if( a_index < b_index ) 
    alert( 'a is first' );
else
    alert( 'b is first' );

For a browser compliant non-jQuery solution, you could do this:

function sortInDocumentOrder( a, b ) {
    var all = document.getElementsByTagName('*');

    for( var i = 0; i < all.length; ++i ) {
        if( all[i] === a )
            return [a,b];  
        else if( all[i] === b )
            return [b,a];
    }
}

Give it two elements, and it will return them in the document order.

var a = document.getElementById('a');
var b = document.getElementById('b');

var inOrder = sortInDocumentOrder( a, b );

You're confusing a few things. The location in markup is the location in the DOM. This line shows your confusion:

<x><y /></x> //not really before or after is it?

Of course y is after x by any reasonable definition. You should think of the DOM as a tree, not as characters in a text file.

Now, as for determining position, use Node.compareDocumentPosition:

node.compareDocumentPosition(otherNode)

The return value is a bitmask with the following values:

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

Yes, sort of. DOM3 introduced Node.compareDocumentPosition, which allows you to compare the position of two elements. The functionality isn't very friendly: it involves bitmasks: this is a jQuery plugin that should simplify its use.

This code is only tested on Firefox 9 and the current version of Chromium. Certainly it won't work in old versions of IE.

$.fn.docPosition = function(element) {
    if (element.jquery) element = element[0];

    var position = this[0].compareDocumentPosition(element);

    if (position & 0x04) return 'after';
    if (position & 0x02) return 'before';
};

Also, an element that contains another is considered to be before it in the structure.


OK, a little Googling gives me this blog post by John Resig (the creator of jQuery), which includes compatibility with IE <9. (It's a little ugly: it uses two non-standard bits of functionality: contains and sourceIndex.) This code should be cross-browser:

$.fn.docPosition = function (element) {
    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;
    }

    if (element.jquery) element = element[0];

    var position = comparePosition(this[0], element);

    if (position & 0x04) return 'after';
    if (position & 0x02) return 'before';
};