7

For example, I have var menu_ready = false;. I have an ajax function that sets menu_ready to true when the ajax stuff is done:

//set up event listener here

$(...).load(..., function() {
    ...
    menu_ready = true;
}

How do I set up an event listener that waits for menu_ready to be true?

trusktr
  • 34,715
  • 41
  • 148
  • 226
  • 1
    Why not adding a 'if(menu_ready) { *code you want to call on the event listener* }' right after the ajax callback returns ? – Cystack Jul 21 '11 at 23:05
  • Agree with Cystack, except you don't even need the `if` given that you know your ajax callback is setting `menu_ready = true;` - call your code directly within the ajax callback. – nnnnnn Jul 22 '11 at 01:09
  • 1
    The code may not always occur directly after the ajax code. I just need it to execute whenever it so happens that menu_ready is true, regardless of the location of the code. – trusktr Jul 22 '11 at 01:20
  • possible duplicate of [Listening for variable changes in JavaScript or jQuery](http://stackoverflow.com/questions/1759987/listening-for-variable-changes-in-javascript-or-jquery) – Gideon Jun 25 '15 at 02:30
  • Wow, I was such a newb at the time. – trusktr Nov 16 '19 at 04:59

7 Answers7

9

You can't attach event listeners to JavaScript variables per se, but you can fake it. Instead of a boolean var, use an object with get, set, and listen methods:

function Bool(initialValue) {
    var bool = !!initialValue;
    var listeners = [];
    var returnVal = function(value) {
        if (arguments.length) {
            var oldValue = bool;
            bool = !!value;
            listeners.forEach(function (listener, i, list) {
                listener.call(returnVal, { oldValue: oldValue, newValue: bool });
            });
        }
        return bool
    };
    returnVal.addListener = function(fn) {
        if (typeof fn == "function") {
            listeners.push(fn);
        }
        else {
            throw "Not a function!";
        }
    };
    return returnVal;
}

You'd use it like this:

var menu_ready = Bool(false);
if (menu_ready()) {
    // this code won't get executed, because menu_ready() will currently return false;
}
menu_ready.addListener(function (e) {
    if (e.oldValue != e.newValue) {
        // value changed!
    }
});
menu_ready(true);  // listeners are called.
gilly3
  • 78,870
  • 23
  • 132
  • 169
  • Interesting. I don't completely understand it yet though. It seems that basically when the menu_ready() function receives a certain imput (argument) that then the specified function gets executed based on what you set it to with .addListener. Its almost as if we are doing object oriented programming here and storing a function inside the object and that when the argument `true` is passed in it cuased the stored function to execute. Only difference from OOP is it is not a class, but a function. – trusktr Jul 22 '11 at 02:41
  • 1
    It was hard to follow but thanks! I guess I need to read up on the listener stuff. :D Jonathan M's solution with "continuous polling" was very simple a worked perfect! – trusktr Jul 22 '11 at 02:41
4

Since this is a pretty old question and many of these answers are out of date, I thought I'd add a more current answer.

Background

Javascript is an event driven language. So, any change that occurs in the environment at any time other than when the page is first loaded is caused by some sort of event. As such, the best way to communicate changes is to use events. You either listen for the same event that caused the change you want to watch or you create a new event to let people know when something specifically has changed.

Node.js has an eventEmitter object that is very useful and is pure Javascript (no DOM involved). That same code can also be used in the browser if one wants a general purpose event mechanism that is independent of the DOM.

The browser DOM has it's own event notification system where you can create events, trigger events and listen for events.

Polling for changes (as the accepted answer here shows) is generally a "hack" that is inefficient, can miss changes, is not necessarily timely, wastes battery and CPU, etc... If you control the code, there is always a better way to design it than polling.

The OP's Actual Problem

There still is not efficient mechanism of monitoring a variable change, but the OP's problem was actually that someone wants to know when this code gets triggered:

$(...).load(..., function() {
    ...
    menu_ready = true;
}

Of course, one simple answer is that anyone who wants to know when that is ready could also just monitor the same thing this is with $(...).load(), but that would somewhat tie their notification to how this was specifically implemented.

Creating Our Own Event Notification That Anyone Can Listen For

Instead, what would be better would be to create our own event and when we know the menu is ready, we trigger that event. Then anyone, anywhere in the code can listen for that same event.

The browser already has an extensible event system built in as described here. So, we can use that here. You need to pick some well-known object to trigger the event on that exists at the time any listener would want to register to know about this. Since the OP didn't show any specific context, I'll just pick the window object.

$(...).load(..., function() {
    ...
    menu_ready = true;

    // create new event
    var event = new Event('menu_ready');

    // Dispatch the event.
    window.dispatchEvent(event);        
 }

Note, you can use any DOM object as the source of the event (whatever makes the most sense for your application) as long as it exists at both the time someone wants to register for the event and the time at which the event occurs.

Then, a listener anywhere in your code can listen for this event:

 window.addEventListener("menu_ready", function() {
     console.log("menu is now ready");
 });

Working Demo

Here's an actual working implementation that triggers the event upon a button press.

let button = document.getElementById("pressme");

button.addEventListener("click", function() {
    // create new event
    var event = new Event('menu_ready');

    // Dispatch the event.
    window.dispatchEvent(event);        

});

function log(x) {
    var div = document.createElement("div");
    div.innerHTML = x;
    document.getElementById("log").appendChild(div);
}


// then create a listener
window.addEventListener("menu_ready", function() {
    log("Got menu_ready event");
});
#log {margin-top: 50px;}
<button id="pressme">
Press Me
</button>
<div id="log">

</div>

I crafted this answer to match the original problem illustrated in the question here. For reference, there are several other useful mechanisms of seeing changes, but they generally rely on properties of an object, not just a simple variable.

See all the answers on this question: Listening for variable changes. But specifically make sure you read these two:

Using a proxy object to track changes

Using getters and setters to track changes

jfriend00
  • 580,699
  • 78
  • 809
  • 825
2

There is no cross-browser (cross-platform) event which gets that job done. There are some pretty specific mechanisms to watch objects propertys for instance, but nothing to watch out for booleans (imo).

You want to execute a callback function aswell at the time you're setting that variable to true. You can also apply some jQuery sugar:

function myCallbackReference() {
    alert('yay');
}

$('#foobar').load('/some/code', function() {
    menu_ready = true;
}).done( myCallbackReference );
jAndy
  • 212,463
  • 51
  • 293
  • 348
  • Only problem with that is that `.done()` can execute code before everything it the `.load()` callback is finished executing if there is asynchronous stuff in there. hmmmmmmmmm – trusktr Jul 22 '11 at 01:24
  • Isn't putting `myCallbackReference` inside `.done()` the same thing as calling `myCallbackReference` inside the callback for `.load()` right after `menu_ready = true;`? – trusktr Jul 22 '11 at 01:28
  • In other words, jAndy, `myCallbackReference` will execute at the same time as the callback for `.load()`, which isn't exactly what I want. `.done()` will execute asynchronously with `.load()`'s callback. – trusktr Jul 22 '11 at 02:15
  • @trusktr: I truly believe that all the events from the Deferred object are fired *after* the success/complete handler you pass into an ajax call. – jAndy Jul 22 '11 at 08:57
1

Why do not create a function menuReady that will be fired when you want ?

menuReady();
ChristopheCVB
  • 7,038
  • 1
  • 27
  • 54
1
function menuReady(){
  // Do whatever it is you need done when menu is ready
}    

$(...).load(..., function() {
  menuReady();// menuReady is called on callback    
});
Lou Groshek
  • 573
  • 4
  • 18
0
Utils = {
    eventRegister_globalVariable : function(variableName,handlers){
        eventRegister_JsonVariable(this,variableName,handlers);
    },
    eventRegister_jsonVariable : function(jsonObj,variableName,handlers){
        if(jsonObj.eventRegisteredVariable === undefined) {
            jsonObj.eventRegisteredVariable={};//this Object is used for trigger event in javascript variable value changes ku
        }
        Object.defineProperty(jsonObj, variableName , {
                    get: function() { 
                        return jsonObj.eventRegisteredVariable[variableName] },
                    set: function(value) {
                        jsonObj.eventRegisteredVariable[variableName] = value; handlers(jsonObj.eventRegisteredVariable[variableName]);}
                    });
            }
Avatar
  • 29
  • 2
-3

WARNING My answer below is from a period slightly prior to the sailing of the Mayflower. It's a bad answer. I can't remove it because it was accepted, and the system won't let me remove an accepted answer. I could edit it to make it better, but why do that when others have given solid answers below? Bottom line: scroll past this to a more fulfilling life. :)

<BAD-ANSWER>

One way is continual polling:

function checkMenu() {
    if (!menu_ready) {
        setTimeout("checkMenu();", 1000);
        return;
    } else {
        // menu_ready is true, so do what you need to do here.
    }
}

and...

<body onload="checkMenu();">
</BAD-ANSWER>
Jonathan M
  • 16,131
  • 8
  • 49
  • 88
  • 4
    no no no.... not a good idea.... if you can't control the boolean just attach a DOM manipulation event listener or some other listener. – Joseph Marikle Jul 21 '11 at 23:15
  • 1
    @Joseph can you provide details on that? – trusktr Jul 22 '11 at 01:26
  • 2
    @Jonathan, actually I think `setTimeout("checkMenu();", 1000);` in your example should be `setTimeout(checkMenu, 1000);` to work, but I get what you mean. – trusktr Jul 22 '11 at 01:48
  • Thanks Jonathan, this was a good solution. It worked for me. :D It's definitely a good way to detect when other variables change all while remaining asynchronous. Two thumbs up. – trusktr Jul 22 '11 at 02:09
  • 1
    A variable can change many times in 1000 ms. This may be ok for some situations, but If true event listening is desired, this will be buggy. – gilly3 Jul 22 '11 at 03:45
  • @gilly: Yeah, he can change the 1000 to whatever he wants if higher resolution response is needed. – Jonathan M Jul 22 '11 at 15:38
  • In my case, the event happens only once, so this works perfect! – trusktr Sep 03 '11 at 11:45
  • Polling a variable is pretty much never the best solution in node.js as it's particularly inefficient and not very real time. Node.js is an event driven solution. Whatever changes the variable should trigger an event which anyone can subscribe to in order to know the value changed. – jfriend00 Nov 15 '19 at 16:37
  • Hi, @jfriend00, this question and answer are from 2011, and it didn't mention node.js. Certainly now, in 2019, there are better ways than mentioned here. – Jonathan M Nov 15 '19 at 20:54
  • @JonathanM - Same for a browser. Javascript is event driven. Polling a variable was never a good design, not even back in 2011. – jfriend00 Nov 15 '19 at 21:00
  • @jfriend00, as noted 8 minutes after the answer was posted. :) I agree. – Jonathan M Nov 15 '19 at 21:24
  • @JonathanM - ok, you agree that your answer is never a good idea (yet you posted it). Not sure what we disagree on. I added my comment as an opinion on the desirability of this answer. – jfriend00 Nov 15 '19 at 21:27
  • @jfriend00, My post-2011 self agrees. Perhaps you could contribute a good answer for 2019. All of these answers are quite stale. – Jonathan M Nov 15 '19 at 21:30
  • I added [my own answer](https://stackoverflow.com/a/58885921/816620) to this question. – jfriend00 Nov 15 '19 at 23:31