civiltomain
civiltomain

Reputation: 1166

Run a function that calls itself every 50 ms and return value when a condition is true

I want a code able to know if certain html element is present. I use a recursive call inside timeout. Maybe can be another way to make this, but I'm interested to know how to write this code.

if (exist("an_id")!==null)
{ ..... }

function exist (id)
{ times =20;
  var el;
 function get_el (callback) 
 { 
    el =  document.getElementById(_id);      
    if (el==null )
    {
           if (--times==0) 
           callback();     
           else
            setTimeout( function () {get_el()}, 50);
    }
    else
    callback();
 }
 get_el ( function (){return el;}); // first call. pass callback.
}

This code apparently run ok, 'el' is detected or not after the 'times' specified, but function 'exist' does not return nothing.

I'm some confused. Any help would be appreciated.-

Upvotes: 0

Views: 232

Answers (2)

user4698813
user4698813

Reputation:

Well, you did say that you're interested in finding out how to write the code in the way you were doing it, so I made some modifications, but tried to keep it as similar to your code as possible.

The most relevant change I made was passing the callback to exist instead of to get_el. This way, the callback function is 'kept alive' until the recursive timeout finishes. You need to run your "this element exists" code inside of the callback, though, since this is "asynchronous".

I also changed some of the indentation/addes braces, since your code was quite confusing for me:

http://jsfiddle.net/bro2nvtz/4/

function exist(id, callback) {
    var times = 20;
    var el;
    // Added some more console longs and additional calls to `callback`
    // so it's easy to keep track of what's going on
    function get_el() {
        el = document.getElementById(id);
        if (!el) {
            console.log("Trying... " + times);
            callback(el);
            if (--times == 0) {
                console.log("Giving up...");
                callback(el);
            } else {
                setTimeout(function() {
                    get_el()
                }, 50);
            }
        } else {
            console.log("Found it!");
            callback(el);
        }
    }
    get_el();
}
// The way you were doing things, the callback was only getting passed once, and then, it was undefined.
// I think it's a better idea to pass it to the `exist` function, so that it persists as long as
// the timeout is still running.
// For some reason, you were also using _id instead of id. Typo?
exist('dinosaur', function(el) {
  
    // Inside of this callback, run the code that you wish to run
    // when the element exists (or when it doesn't).
    console.log( 'Is it there?: ' + (el ? 'yes! id: ' + el.id : 'no') );
  
    // If it exists...
    if(el) {
      console.log("+------------------------------+");
      console.log("I know for sure it exists, yay!");
      console.log("+------------------------------+");
      setTimeout(function () { 
        el.style.opacity = '1';
      }, 200);
    }
  
    window.scrollTo(0, document.body.scrollHeight);
  
});

// For testing...
// Logger and a timeout that appends the img after 500ms

(function () {
    var old = console.log;
    var logger = document.getElementById('log');
    console.log = function (message) {
        if (typeof message == 'object') {
            logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(message) : message) + '<br />';
        } else {
            logger.innerHTML += message + '<br />';
        }
    }
})();

// Creates and appends an img of id `dinosaur` to the body, after 500ms.
// Increase the timeout to something like 5000ms and your script will give up.
setTimeout(function () {
    var dinosaur = document.createElement('img');
    dinosaur.id = 'dinosaur';
    dinosaur.src = 'http://www.enchantedlearning.com/tgifs/Trexskelanim.gif';
    dinosaur.style.cssText = 'opacity: 0; transition: all 500ms;';
    document.body.appendChild(dinosaur);
}, 750);
html, body {
    background: #000;
}
pre {
    line-height: 1.5em;
    color: lightblue;
}
<pre id="log"></pre>

Upvotes: 0

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276306

Well, you can't just use the return value of a synchronous method. Your condition will always evaluate to false.

You can encapsulate it in a function that returns a promise or takes a callback:

function exists(id){
    return new Promise(function(resolve, reject){ // old browsers need polyfill
        setTimeout(function check(){ 
           if(document.getElemenyById(id)) resolve();
           else setTimeout(check, 50);
        }, 50);
    });
}

Which would let you do:

exists("someID").then(function(){
   // here the element exists
});

Upvotes: 2

Related Questions