Skip to content

Instantly share code, notes, and snippets.

@chrisjhoughton
Last active October 6, 2023 09:46
Star You must be signed in to star a gist
Save chrisjhoughton/7890303 to your computer and use it in GitHub Desktop.
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
});
@TGilany
Copy link

TGilany commented Apr 10, 2020

Thank you!

@cadot-info
Copy link

thank you!

@LukeSavefrogs
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

schroef commented Dec 17, 2020

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

@schroef
Copy link

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
Copy link

@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
Copy link

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
Copy link

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);
};

@jatieu
Copy link

jatieu commented Dec 27, 2021

View short and full

//Check
let check = (e, m, t, c) => { //(element, max, timeout, callback)
    let i = +m,
    loop = () => { $(e).length ? c() : --i && setTimeout(() => { loop() }, t) }
    loop()
},            
//Process
myFunc = () => { //Do something }
//Usage
check("element", 30, 500, myFunc)

@michaelyeg
Copy link

Works very well! Thank you! You are a life saver to my project

@AMDarter
Copy link

AMDarter commented Mar 1, 2022

Thank you!
I set maxTimes to 100. After 100 tries, it will stop.

jQuery(document).ready(function () {
    var selector = '#element';
  
    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);
        }
      }
    };
  
    waitForEl(selector, function () {
        jQuery(selector); // do something with selector
    }, 100);
});

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