简体   繁体   中英

Better (more performant) way to get specific DIVs?

I want to add a specific DIV to other DIVs of a defined class. Because the page changes regularly I do this on every DOM-Change. This happens quite often and there are a lot of DIVs (up to a few 1000) that meet the criteria.

(This is an extension so I cannot modifiy the source)

I do it this way:

$('.Qg').each(function() {
  if ($(this).parent().find('.quickShare').length === 0)
  {      
    $(this).before('<div class="quickShare">(some more html)<br/></div>');
  }
});

That works but does not seem to be very performant, mainly because of the "each" - Loop

Is there a more elegant (and especially performant) way to get only those DIVs which's parent do not contain my DIV (something like $('.Qg').parent().without('quickShare').each(function(){}); (pseudocode)?

Update: To make it clearer a DOM-Example:

<div class="anOuterDiv>
  <div class="Qg">something here</div>
</div>

<div class="anotherOuterDiv">
  <div class="quickShare">already added</div>
  <div class="Qg">something here</div>
</div>

I want to Add the "quickShare" div before the "Qg", but only if it does not exist. (So I want to get the upper Qg, but not the lower Qg)

Give all the parents of .Qg the class QgContainer , then do:

$(".QgContainer:not(:has(.quickShare)) > .Qg").each(function() {
    ...
});

Since you can't change the site, try:

$(".Qg").filter(function() {
    return $(this).siblings(".quickShare").length == 0);
}).each(function() {
    ...
});

You can filter each .Qg that's not preceded by a .quickShare sibling and then apply .before() on that:

$('.Qg')
  .filter(function() {
    var node = this.previousSibling; // start with previous sibling

    while (node) {
      if (node.className == 'quickShare') {
        return false; // we found one
      }
      node = node.previousSibling; // keep trying with previous sibling
    }

    return true;
  })
  .before('<div class="quickShare">(some more html)<br/></div>');

As you wanted better(more perfomant) then you could consider using pure Javascript.

HTML

<div class="anOuterDiv1">
    <div class="Qg">something here</div>
</div>
<div class="anOuterDiv2">
    <div class="quickShare">already added</div>
    <div class="Qg">something here</div>
</div>
<div class="anOuterDiv3">
    <div class="Qg">something here</div>
</div>
<div class="anOuterDiv4">
    <div class="quickShare">already added</div>
    <div class="Qg">something here</div>
</div>

Javascript

Array.prototype.forEach.call(document.getElementsByClassName('Qg'), function (Qg) {
    var parentNode = Qg.parentNode,
        quickShares = parentNode.getElementsByClassName('quickShare'),
        newQuickShare;

    if(!quickShares.length) {
        newQuickShare = document.createElement('div');
        newQuickShare.className = 'quickShare';
        newQuickShare.textContent = 'Newly added';
        parentNode.insertBefore(newQuickShare, Qg);
    }
});

On jsFiddle

Next we should actually compare it against some jQuery, so we will use the accepted answer.

$(".Qg").filter(function() {
    return $(this).siblings(".quickShare").length == 0;
}).each(function() {
    $(this).before('<div class="quickShare">Newly added</div>');
});

On jsFiddle

And now lets see how they perform on jsPerf

This time it will definitely work:

 $('div:only-child.Qg').each(function(){
   $(this).before('<div class="quickShare">(some more html)<br/></div>');
 });

Try this. This is very easy and readable and small and performant.

jsFiddle Demo http://jsfiddle.net/VS6mG/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM