compareDocumentPosition plugin for jQuery
Being able to quickly compare two elements' positions in the browser is extremely useful for a variety of tasks. The DOM Level 3 specification describes the compareDocumentPosition method. It returns a bitmask with a whole bunch of useful information. But not all browsers support it :(
For a really solid walkthrough of compareDocumentPosition, check out John Resig's Comparing Document Position article. In this article, he gets close to providing a fast compareDocumentPosition for all browsers. JavaScriptMVC's standalone jquery.compare plugin steals his ideas and DOES provide a fast compareDocumentPosition in all the browsers jQuery supports.
Download
jquery.compare.js (minified 0.8kb) [works with all versions of jQuery]
Demo
Documentation
JavaScriptMVC's jquery/dom/compare docs.
Use
To use the compare plugin, just call compare on your jQuery wrapped elements with another element or jQuery element:
$('#foo').compare( $("#bar") ) //-> Number
The number is actually a bitmask that represents multiple several pieces of relational information about the two elements. Here's what each bit means:
| Bits | Number | Meaning |
|---|---|---|
| 000000 | 0 | Elements are identical. |
| 000001 | 1 | The nodes are in different documents (or one is outside of a document). |
| 000010 | 2 | Node B precedes Node A. |
| 000100 | 4 | Node A precedes Node B. |
| 001000 | 8 | Node B contains Node A. |
| 010000 | 16 | Node A contains Node B. |
This means if a compare results in a 10, node B is before and contains node A. Typically, you just want to know one bit's information. To do this, use the bitwise AND operator (&) like:
if( $('#foo').compare( $("#bar") ) & 2 ){
//do something because #bar is before #foo
}
How it works
The plugin uses all the techniques in John's article, but fixes the 'Safari' problem. Older versions of Safari lack compareDocumentPosition and sourceIndex. This makes comparing node order (the 2 and 4 values of the bitmask) challenging. A naive approach might be to walk up the parent nodes until an intersection happens. But, as I've learned from event delegation, calling parentNode is surprisingly slow. But using document Ranges and comparing them is fast. The following code adds the 2 and 4 values in Safari:
var range = document.createRange(),
sourceRange = document.createRange(),
compare;
range.selectNode(this[0]);
sourceRange.selectNode(b);
compare = range.compareBoundaryPoints(Range.START_TO_START,
sourceRange);
number += (compare === -1 && 4)
number += (compare === 1 && 2)
This code uses Safari's implemented compareBoundryPoints method in the DOM Level 2 Traversal-Range recommendation. CompareBoundaryPoints compares 2 ranges. You have to provide which parts of the ranges to compare and it returns -1,0, or 1 to indicate the the boundary positions.
Subscribe to:
Related Content
About Jupiter
Jupiter is dedicated to making JavaScript an easy and enjoyable place to develop kick ass apps. We open-source everything and provide expert web application development, support, and training.
Recent Posts
- Significant Whitespace
- 3.2 $.Controller - Templated Event Binding
- Deferreds and 3.1
- jQuery Resize Event
- FuncUnit Humor
- Having your Cake and Eating it without Getting Fat
- Why You Should Never Use jQuery Live
- Knock JavaScriptMVC's Back Out
- Advanced jQuery Training at SF jQuery Conf 2011
- JavaScriptMVC and List Performance
JavaScript development, design, and consulting.