views:

59

answers:

3

I'm looking for a nice way to mark/select all elements between two selected elements.

Imagine

<parent>
  <p>...</p>
  <p>...</p>
  <p>...</p>
  <p>...</p>
</parent>

There's a click handler on parent. The user now can select two p element in this list an then all p-elements in between should get 'activated'.

I'm thinking about a system like:

  1. first click: mark/remember first element -> A
  2. second click: mark/remember second element -> B
  3. determine whether or not A is before B
  4. do a A.nextUntil(B) (unless B is before A)

I have no idea how to do 3, expect the brute force approach (iterate in both directions and see if it is there)

  • Does the dom internally know what element comes before another?
  • Are there any nicer ideas?

About my situation: parent could contain several thousand p's.

Thanks for your help/ideas!

Reto

+4  A: 

To determine which element comes first you could simply do:

$(a).index() < $(b).index()

Or, a slightly faster approach:

$(a).prevAll().length < $(b).prevAll().length

Note, both of these approaches will only work properly if a and b have the same parent.


About my situation: parent could contain several thousand p's.

How are the <p>s added? Maybe you could give them each an ID corresponding to their position (e.g. p1, p2...) -- this would definitely save you from having to determine their positions using the above approaches.

J-P
very interesting, but you say index() is slower than prevAll().length??
reto
i see, for index I have to select all elements.. *Hm*
reto
Yup, `index()` is much slower (see [this](http://james.padolsey.com/jquery/index)) -- it does an `indexOf` check in the `$(this).parent().children()` collection. `prevAll().length` is much faster.
J-P
thx! I probably will go for the prevALl.length() approach until I see further problems.. and if that happens I'll do some indexing after the dom has been loaded, should be okay :).
reto
+1 for the ID's suggestion. Will be a real time saver considering there may be thousands of these.
Anurag
+1  A: 

There is a method compareDocumentPosition defined in DOM level 3 to compare the position of two DOM nodes.

You would use it as:

firstPara.compareDocumentPosition(otherPara);

If the return value is 2 or Node.DOCUMENT_POSITION_PRECEDING, then firstPara comes before otherPara.

There is also a jQuery plugin for it.

I like @J-P's approach of adding some identifier to quickly determine their position without looking at any other elements. HTML5's data attributes are also an option for storing this value.

<parent>
  <p data-pos="1">..</p>
  <p data-pos="2">..</p>
  ..
</parent>

Access as $(e).attr('data-pos')

Anurag
+1  A: 

hey Reto here's a solution i cooked up in jquery, hope it helps:

 <div id="test">
          <p>abc</p>
          <p>def</p>
          <p>ghi</p>
          <p>jkl</p>
        </div>

    <script>
    var a = $('#test').find('>p');
    var cli = [], range;
    $('#test').delegate("p",'click', function(event){
            if (cli.length < 2){//if there are less than 2 clicks in the queue
              cli.push(a.index(event.target));
            }
            else{//reset queue and recall the function
                cli = [];
                arguments.callee(event);
            }

            if (cli.length == 2){//if there are 2 clicks
             //filter from the initial selection only the elements between the two clicks
             range = a.filter(":lt("+cli[1]+"):gt("+cli[0]+")");
             if (cli[0]<cli[1]) {//aply some style to highlight and then revert
              range.css({color:'red'});
              setTimeout(function(){range.css({color:'black'})}, 1000);
          }
            }
    });
    </script>
Ionut Popa
thank you! chrs reto
reto