Check if element is before or after another element in jQuery

JqueryDom

Jquery Problem Overview


Let us suppose I have this markup

<h3 id="first">First</h3>
<p>Hi</p>
<p>Hello</p>
<h3 id="second">Second</h3>
<p>Bye</p>
<p>Goodbye</p>

How can I programmatically check if an element, such as one of the ps, is after first h3 and before the second h3, with jQuery?

I am trying to do something like this:

$(p).each(function(){
   if($(this).isAfter("#first") && $(this).isBefore("#second")) {
     // do stuff
   }
});

Jquery Solutions


Solution 1 - Jquery

Rocket's solution works fine if sel is a real selector, if you pass a jquery object though, it won't work.

If you want a plugin that works with both selectors and jquery objects, just use my slightly modified version instead:

(function($) {
    $.fn.isAfter = function(sel){
        return this.prevAll().filter(sel).length !== 0;
    };

    $.fn.isBefore= function(sel){
        return this.nextAll().filter(sel).length !== 0;
    };
})(jQuery);

Kudos to Rocket for the original solution.

Solution 2 - Jquery

To see if an element is after another, you can use prev() or prevAll() to get the previous element(s), and see if it matches.

And to see if an element is before another, you can use next() or nextAll() to get the next element(s), and see if it matches.

$.fn.isAfter = function(sel){
  return this.prevAll(sel).length !== 0;
}
$.fn.isBefore= function(sel){
  return this.nextAll(sel).length !== 0;
}

DEMO: http://jsfiddle.net/bk4k7/

Solution 3 - Jquery

You can use the index() function:

$('p').each(function(){
   var index = $(this).index();
   if(index > $("#first").index() && index < $("#second").index()) {
     // do stuff
   }
});

Solution 4 - Jquery

Here is a fiddle

$('p').each(function() {
    previousH3 = $(this).prevAll('h3');
    nextH3 = $(this).nextAll('h3');

    if (previousH3.length > 0 && nextH3.length > 0) {
        $(this).css('color', 'red')
    }

});

Solution 5 - Jquery

I implemented a general isAfter function, which considers the depth and DOM tree and can correctly determine if a is after b even if they are in 2 different subtrees:

<div>
  <div>
    <p class="first">I come first</p>
  </div>
</div>

<div>
  <div>
    <p class="second">I come second</p>
  </div>
</div>

you can find it in my GitHub Repository. Would appreciate your feedback on the code

Solution 6 - Jquery

The answer:

$.fn.isAfter = function(sel){
    return this.prevAll(sel).length !== 0;
}
$.fn.isBefore= function(sel){
    return this.nextAll(sel).length !== 0;
}

Was selecting all previous elements or next elements regardless of the selector (sel) so I made the following modifications that select elements based on class:

$.fn.isAfter = function(sel){
    sel = "." + sel.attr("class").replace(/ /g, ".");

    return this.prevAll(sel).length !== 0;
}
$.fn.isBefore= function(sel){
    sel = "." + sel.attr("class").replace(/ /g, ".");

    return this.nextAll(sel).length !== 0;
}

Solution 7 - Jquery

This one-liner check will work in every case

IDs from your question used for this example:

document.getElementById("first") === document.querySelectorAll("#second, #first")[0]

Order of IDs in querySelectorAll method is not relevant it is only necessary to use selector that will select both elements in this case #second, #first.

If you need to use this multiple times you can use this function:

function isBeforeElement( first_id, second_id ) {
	return document.querySelectorAll('#' + first_id + ', #' + second_id)[0] === document.getElementById(first_id);
}

This function will work without errors even in exceptional cases:

  1. If both elements are not found it will return false
  2. If second element is not found it will return true
  3. If first element is not found it will return false

Solution 8 - Jquery

Keeping in mind Rocket's answer I prefer to use .index() approach like following:

$.fn.isAfter = function(sel){
    return $(this).index() > $(sel).index();
};
$.fn.isBefore= function(sel){
    return $(this).index() < $(sel).index();
};

It seems more clear for reading/understanding and works perfectly in rows selection.

Solution 9 - Jquery

The previous answers work fine IF the elements in question are siblings (which they are in the original question) but a more general solution would consider the following scenario:

<div id="top">    
    <div>
        <div>
           <h3 id="first">First</h3>
           <p>Hi</p>
           <p>Hello</p>
        </div>
    </div>
    <div>
        <h3 id="second">Second</h3>
        <p>Bye</p>
        <p>Goodbye</p>
    </div>
</div>

A more general solution would be:

$.extend($.fn, {
    isBefore: function(sel, context) {
        // Get all element within the context.
        var all = $(context || document).find("*");
        return all.index(this[0]) < all.index($(sel));
    },
    
    isAfter: function(sel, context) {
        return !this.isBefore(sel, context);
    }
});

Where "context" is an optional parameter that merely limits the search area for jQuery to improve performance. I haven't performed any tests but "context" is probably unnecessary since $("*") seems to execute qualitatively fast. Also note that index() will return -1 is either element is not within the context.

$("#first").isBefore("#second");  
// Gives the same result as
$("#first").isBefore("#second", "#top");

Of course this all assumes that by "before" and "after" you mean their order in the document model. If you are concerned about their relative positions on the display you would want to examine the elements display coordinates relative to the document as "relative", "fixed" and "absolute" CSS positions make anything possible.

Solution 10 - Jquery

The accepted answer only works if the elements are at the same level. One approach to solve it if you want to locate which element is first regardless of the level is:

  1. Set a temporary attribute to each element with the same value
  2. Use a jquery selector to locate the first element with that attribute value

Solution 11 - Jquery

This version works at any depth comparing if two elements are before, equal, or after each other. Returning -1, 0, and 1 respectively.

How it works is that it gets all the index offsets of the elements passed into the compare function.

Then it compares the two array of offsets and stops iterating when it finds a discrepancy.

var topElement = $("span").eq(0);
var bottomElement = $("span").eq(2);

var res = compare(topElement, bottomElement);

console.log("The compare result is: " + res)



function getOffsets(el)
{
  var res = new Array();
  
  while(el.length > 0)
  {
    res.push(el.index());
    el = el.parent();
	}
  
  return res.reverse();
}

function compare(a, b)
{  
  var offA = getOffsets(a);
  var offB = getOffsets(b);
  
  
  for(let i = 0; i < offA.length + 1; i++)
  {
		let delta = (offA[i] || -1) - (offB[i] || -1);
    
    if(delta < 0)
      return -1;
    else if(delta > 0)
      return 1;
  }
  
  return 0;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div>
	<span>1</span>
</div>
<div>
	<div>
		<span>2</span>
	</div>
</div>
<div>
	<div>
		<div>
			<span>3</span>
		</div>
	</div>
</div>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeter OlsonView Question on Stackoverflow
Solution 1 - JqueryMarco FucciView Answer on Stackoverflow
Solution 2 - JqueryRocket HazmatView Answer on Stackoverflow
Solution 3 - JqueryDennisView Answer on Stackoverflow
Solution 4 - JqueryAmin EshaqView Answer on Stackoverflow
Solution 5 - JqueryArash DalirView Answer on Stackoverflow
Solution 6 - JqueryPeterView Answer on Stackoverflow
Solution 7 - JqueryŠimeView Answer on Stackoverflow
Solution 8 - JquerySergei DubininView Answer on Stackoverflow
Solution 9 - JquerymoomooView Answer on Stackoverflow
Solution 10 - JqueryJoeView Answer on Stackoverflow
Solution 11 - JqueryJohnView Answer on Stackoverflow