Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Wait for an element to exist on the page with jQuery
var waitForEl = function(selector, callback) {
if (jQuery(selector).length) {
callback();
} else {
setTimeout(function() {
waitForEl(selector, callback);
}, 100);
}
};
waitForEl(selector, function() {
// work the magic
});
@trananhmanh89

This comment has been minimized.

Copy link

@trananhmanh89 trananhmanh89 commented Aug 3, 2016

Awesome!

@scr4bble

This comment has been minimized.

Copy link

@scr4bble scr4bble commented Mar 6, 2017

👍 nice and simple

@yngrdyn

This comment has been minimized.

Copy link

@yngrdyn yngrdyn commented Mar 21, 2017

Thank you!

@luhaoming

This comment has been minimized.

Copy link

@luhaoming luhaoming commented Jun 1, 2017

thank you~

@smithbrianscott

This comment has been minimized.

Copy link

@smithbrianscott smithbrianscott commented Jun 19, 2017

Exactly what I needed! Thanks

@QwertyZW

This comment has been minimized.

Copy link

@QwertyZW QwertyZW commented Oct 6, 2017

That looks like a stackbomb

function waitForEl(selector, callback){
    var poller1 = setInterval(function(){
        $jObject = jQuery(selector);
        if($jObject.length < 1){
            return;
        }
        clearInterval(poller1);
        callback($jObject)
    },100);
}
@mordini

This comment has been minimized.

Copy link

@mordini mordini commented Jan 22, 2018

I agree about the stackbomb, because I made something similar and found out the hard way :D

Here's a version that will only iterate by a limited amount, though it isn't clearing the interval like QwertyZW's is...

var waitForEl = function(selector, callback, count) {
  if (jQuery(selector).length) {
    callback();
  } else {
    setTimeout(function() {
      if(!count) {
        count=0;
      }
      count++;
      console.log("count: " + count);
      if(count<10) {
        waitForEl(selector,callback,count);
      } else {return;}
    }, 100);
  }
};

var selector = $("#Whatevs");

waitForEl(selector, function() {
  // work the magic
  console.log("yay");
});
@laromicas

This comment has been minimized.

Copy link

@laromicas laromicas commented Jan 27, 2018

I made this one, I read is better for modern browsers.

var waitForEl = function(selector, callback) {
	if (!jQuery(selector).size()) {
		setTimeout(function() {
			window.requestAnimationFrame(function(){ waitForEl(selector, callback) });
		}, 100);
	}else {
		callback();
	}
};
@dagda1

This comment has been minimized.

Copy link

@dagda1 dagda1 commented Feb 13, 2018

Here is a version that uses promises:

export const wait = (selector) => {
  return new Promise((resolve, reject) => {
    const waitForEl = (selector, count = 0) => {
      const el = jQuery(selector);

      if (!!el) {
        resolve(el);
      } else {
        setTimeout(() => {
          count++;

          if (count < 10) {
            waitForEl(selector, count);
          } else {
            reject();
          }
        }, 100);
      }
    };

    waitForEl(selector);
  });
};
@superhero1

This comment has been minimized.

Copy link

@superhero1 superhero1 commented Mar 1, 2018

@dagda1 how do i use that? Can you give me an example please?

@jcoyle37

This comment has been minimized.

Copy link

@jcoyle37 jcoyle37 commented Mar 29, 2018

@mordini In your example, var selector = $("#Whatevs"); should be var selector = "#Whatevs"; ...otherwise great function, thank you!

@talent3310

This comment has been minimized.

Copy link

@talent3310 talent3310 commented May 23, 2018

Awesome!

@skiza

This comment has been minimized.

Copy link

@skiza skiza commented May 24, 2018

I use this with max times optional ^^. thank you

  var waitForEl = function (selector, callback, maxTimes = false) {
      if (jQuery(selector).length) {
        callback();
      } else {
        if (maxTimes === false || maxTimes > 0) {
          (maxTimes != false) && maxTimes-- ;
          setTimeout(function () {
            waitForEl(selector, callback, maxTimes);
          }, 100);
        }
      }
    };
@nebaughman

This comment has been minimized.

Copy link

@nebaughman nebaughman commented Jul 5, 2018

Thanks for the gist. Below is a mashup of @QwertyZW's version (using setInterval) and maxtries (ala @skiza), plus interval param:

/**
 * Wait for the specified element to appear in the DOM. When the element appears,
 * provide it to the callback.
 *
 * @param selector a jQuery selector (eg, 'div.container img')
 * @param callback function that takes selected element (null if timeout)
 * @param maxtries number of times to try (return null after maxtries, false to disable, if 0 will still try once)
 * @param interval ms wait between each try
 */
waitForEl(selector, callback, maxtries = false, interval = 100) {
  const poller = setInterval(() => {
    const el = jQuery(selector)
    const retry = maxtries === false || maxtries-- > 0
    if (retry && el.length < 1) return // will try again
    clearInterval(poller)
    callback(el || null)
  }, interval)
}
@jthomas077

This comment has been minimized.

Copy link

@jthomas077 jthomas077 commented Aug 30, 2018

To elaborate on these methods of waiting for an element to become available in the DOM, jQuery actually has a couple built-in ways to achieve this - both are promised based:

https://api.jquery.com/jquery.when/

$.when($('.something')).then((self) => { console.log(self); });

https://api.jquery.com/promise/

$('.something').promise().done((self) => { console.log(self); });

Also, considering JavaScript isn't a statically type language, coalescing two types in a single variable can potentially produce unstable results, bloat logic and increase overhead in evaluating expressions, thus by keeping maxTimes / maxtries as a number, you can effectively omit checking for a false value in which would actually also never contain a true value.

Otherwise, some great variations of achieving this... albeit, only if it were a decade ago lol.

Happy coding! :)

@shpoont

This comment has been minimized.

Copy link

@shpoont shpoont commented Dec 22, 2018

@jthomas077
$.when() will immediately resolve anything that is not a Deferred or a Promise.
.promise() will work with animations or custom .queue(), neither of which can be easily used to watch for new element creation.

@sezio

This comment has been minimized.

Copy link

@sezio sezio commented Sep 16, 2019

thanks!

@ahmdaniel

This comment has been minimized.

Copy link

@ahmdaniel ahmdaniel commented Oct 10, 2019

Thanks sharing.

@mohamm6d

This comment has been minimized.

Copy link

@mohamm6d mohamm6d commented Nov 15, 2019

The first one works very fine to me

@UtmostCreator

This comment has been minimized.

Copy link

@UtmostCreator UtmostCreator commented Nov 16, 2019

let waitForSomeElement = () => {
    const container = document.getElementById('element__id');
    if (!container) {
        setTimeout(waitForSomeElement , 50); // give everything some time to render
    }
};
waitForSomeElement ();
@dragontheory

This comment has been minimized.

Copy link

@dragontheory dragontheory commented Nov 20, 2019

CSS's native "live watching" is ideal for this. Preset a rule to watch for a selector or combination of selectors and or conditions. Then dynamically load said selector that makes the CSS rule true and it automagically happens.

  • No JQuery dependency
  • No poling
  • No need for timeouts
  • No counting tries
  • Supports all normative transitions/animations

Something like:

.wrapper {
     .second-parent .column {width: 0;}
}
.wrapper {
     .first-parent .column:has(.loaded) .second-parent .column {width: 100%;}
}

But :has is not supported by any browsers yet.
https://developer.mozilla.org/en-US/docs/Web/CSS/:has

@javiercriado

This comment has been minimized.

Copy link

@javiercriado javiercriado commented Dec 3, 2019

Thank you

@litvinovvo

This comment has been minimized.

Copy link

@litvinovvo litvinovvo commented Dec 26, 2019

In case when you couldn't use simple CSS, better to use Mutation Observer instead of polling: https://gabrieleromanato.name/jquery-detecting-new-elements-with-the-mutationobserver-object

It has great browsers support actually: https://caniuse.com/#feat=mutationobserver

@TGilany

This comment has been minimized.

Copy link

@TGilany TGilany commented Apr 10, 2020

Thank you!

@cadot-info

This comment has been minimized.

Copy link

@cadot-info cadot-info commented Oct 8, 2020

thank you!

@LukeSavefrogs

This comment has been minimized.

Copy link

@LukeSavefrogs LukeSavefrogs commented Oct 18, 2020

Another way of doing for waiting for an element can be using a combination of Promises, try/async and setInterval to check. I made a repository with the code and examples i used in a project of automation using Tampermonkey 😄.

Waiting for an element to exists is as simple as:

(async () => {
    console.log("Operation 1");
    
    // Pause function for until element with id `test` exists
    await SeleniumEngine.waitForElementPresent("#test")

    console.log("Operation 2");
})()

You're free to use it if you find these methods useful 😄

@schroef

This comment has been minimized.

Copy link

@schroef schroef commented Dec 17, 2020

I use this with max times optional ^^. thank you

  var waitForEl = function (selector, callback, maxTimes = false) {
      if (jQuery(selector).length) {
        callback();
      } else {
        if (maxTimes === false || maxTimes > 0) {
          (maxTimes != false) && maxTimes-- ;
          setTimeout(function () {
            waitForEl(selector, callback, maxTimes);
          }, 100);
        }
      }
    };

Where is maxTimes set?

@schroef

This comment has been minimized.

Copy link

@schroef schroef commented Dec 17, 2020

Thanks for the gist. Below is a mashup of @QwertyZW's version (using setInterval) and maxtries (ala @skiza), plus interval param:

/**
 * Wait for the specified element to appear in the DOM. When the element appears,
 * provide it to the callback.
 *
 * @param selector a jQuery selector (eg, 'div.container img')
 * @param callback function that takes selected element (null if timeout)
 * @param maxtries number of times to try (return null after maxtries, false to disable, if 0 will still try once)
 * @param interval ms wait between each try
 */
waitForEl(selector, callback, maxtries = false, interval = 100) {
  const poller = setInterval(() => {
    const el = jQuery(selector)
    const retry = maxtries === false || maxtries-- > 0
    if (retry && el.length < 1) return // will try again
    clearInterval(poller)
    callback(el || null)
  }, interval)
}

Very clean presentation! Im no coder, but i do like that function description you added in the top. Looks like what i see a lot with Python code

@UtmostCreator

This comment has been minimized.

Copy link

@UtmostCreator UtmostCreator commented Dec 17, 2020

I use this with max times optional ^^. thank you

  var waitForEl = function (selector, callback, maxTimes = false) {
      if (jQuery(selector).length) {
        callback();
      } else {
        if (maxTimes === false || maxTimes > 0) {
          (maxTimes != false) && maxTimes-- ;
          setTimeout(function () {
            waitForEl(selector, callback, maxTimes);
          }, 100);
        }
      }
    };

Where is maxTimes set?

I am not sure, but I believe it is the count of attempts

@schroef

This comment has been minimized.

Copy link

@schroef schroef commented Dec 17, 2020

CSS's native "live watching" is ideal for this. Preset a rule to watch for a selector or combination of selectors and or conditions. Then dynamically load said selector that makes the CSS rule true and it automagically happens.

  • No JQuery dependency
  • No poling
  • No need for timeouts
  • No counting tries
  • Supports all normative transitions/animations

Something like:

.wrapper {
     .second-parent .column {width: 0;}
}
.wrapper {
     .first-parent .column:has(.loaded) .second-parent .column {width: 100%;}
}

But :has is not supported by any browsers yet.
https://developer.mozilla.org/en-US/docs/Web/CSS/:has

I do believe Chrome does now. Not sure where it was implemented but foo:has(.true) works just fine

@schroef

This comment has been minimized.

Copy link

@schroef schroef commented Dec 17, 2020

@dagda1

Doesnt seem to work. add a class and the counting keeps going even when the class is loaded in after a while.

@schroef

This comment has been minimized.

Copy link

@schroef schroef commented Dec 17, 2020

@MasterMindNET
how can maxTimes > 0 even work? 0 is false and 1 true or am i missing something?!

@schroef

This comment has been minimized.

Copy link

@schroef schroef commented Dec 17, 2020

Do most of these actually work? I think it will always spit out false because as far as i understand, when DOM is loading and the selector is not there it will be declared as null, its nothing its not there.

Perhaps it will work if the selector is again declared within the timer. Wouldnt it read the tree again and then see it is there?!
I tried a couple of the examples above using a class which gets added when content is loaded into a div. None of them work, they only work if the class is there from the start.

EDIT
Tried my findings and now it does worl. Im no developper or full-time coder. So i properly made lots of mistakes. But now the code works how i want it too.

var selector = $(".tabs-component");
var count = 0;
var waitForEl = function(selector, callback) {
    var selector = $(".tabs-component");
    // if(!count) {
    //     count=0;
    // }
    count++;
    console.log("count: " + count);
    if (selector.length) {
        callback();
        console.log("Yeah");
    } else {
        setTimeout(function() {
            waitForEl(selector, callback);
        }, 1000);
    }
};

waitForEl(selector, function() {
    // work the magic
    console.log("Yeah2");
});

Below screengrabs as a test. original code kept counting, my adjsut shows it see the selector after a while because i declare the name over and over again. Ive seen this behavior before and i believe its well known. Just dont understand why none of the above example think about that?

altered-code
original-code

@UtmostCreator

This comment has been minimized.

Copy link

@UtmostCreator UtmostCreator commented Dec 17, 2020

@MasterMindNET
how can maxTimes > 0 even work? 0 is false and 1 true or am i missing something?!

please try this example:

https://jsfiddle.net/65otf4mL/
or source code:
https://hastebin.com/opaxacawoc.typescript

I will try to answer your questions a bit later!

(maxTimes != false) && maxTimes-- ;

The value will be decremented (maxTimes--) if it is not null, undefined, 0, false.

@schroef

This comment has been minimized.

Copy link

@schroef schroef commented Dec 18, 2020

Thanks for making the example! I got one of the examples working, by declaring the bar for the missing selector in each loop. Otherwise it won't work.

@jackdeguest

This comment has been minimized.

Copy link

@jackdeguest jackdeguest commented Nov 1, 2021

I prefer using a timeout value, so I modified the proposed answer as follows:
This will time out by default after 1 second, or never if a value of 0 is provided.
You can see a codepen here.

var waitForElement = function( selector, callback, interval, timeout )
{
    if( parseInt( timeout ) !== 0 )
    {
        timeout = !isNaN(parseInt(timeout)) || 1000;
    }
    interval = !isNaN(parseInt(interval)) || 100;
    var time = 0;
    var poll = setInterval(function()
    {
        var el = $(selector);
        if( typeof( timeout ) !== 'undefined' && time >= timeout )
        {
            clearInterval( poll );
            return;
        }
        // Try again
        else if( el.length < 1 )
        {
            time += interval;
            return;
        }
        clearInterval( poll );
        callback( el );
    }, interval);
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment