如何创建一个检测布尔变量是否为真的事件监听器?

时间:2011-07-21 23:03:07

标签: javascript events event-handling jquery

例如,我有var menu_ready = false;。我有一个ajax函数,当ajax完成时将menu_ready设置为true:

//set up event listener here

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

如何设置等待menu_ready为真的事件监听器?

8 个答案:

答案 0 :(得分:7)

您无法将事件侦听器附加到JavaScript变量本身,但您可以伪造它。而不是布尔变量,使用具有getsetlisten方法的对象:

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

你会这样使用它:

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.

答案 1 :(得分:2)

没有跨浏览器(跨平台)事件可以完成这项工作。例如,watch对象属性有一些非常具体的机制,但没有什么需要注意的是布尔(imo)。

您希望在将该变量设置为true时执行回调函数。你也可以申请一些jQuery糖:

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

$('#foobar').load('/some/code', function() {
    menu_ready = true;
}).done( myCallbackReference );

答案 2 :(得分:2)

您无法将事件侦听器附加到单个变量。

执行此操作的最佳方法是将变量放在对象中,并使变量的所有用户通过“get”和“set”方法或更具体命名的方法访问它。设置值时,您可以检查该值是否实际以您感兴趣的方式发生变化并采取相应措施。这是一个例子:

var _isMenuReady = false;    // do not access this directly
function isMenuReady() {
    return(_isMenuReady);
}
function setMenuReady(val) {
    if (arguments.length < 1) {
        val = true;
    }
    if (!_isMenuReady && (val)) {
        _isMenuReady = val;
        // _isMenuReady has gone from false to true
        // do whatever you need to do now
    }
    _isMenuReady = val;
}

// sample code
var ready = isMenuReady();    // returns whether the menu is ready or not
setMenuReady();               // sets the menu to be ready now
setMenuReady(false);          // sets it back to not ready

甚至可以使它成为对象的私有成员,因此没有人可以直接在实际的访问器方法之外访问它(尽管如果所有代码都是你的,并且你通过不直接访问变量进行合作,则不需要这样做)。但是,如果您想将其设为私有,请参阅this article以获取有关如何创建私有成员的信息。从本质上讲,您创建了一个持久的函数闭包,其局部变量只能在函数内部访问。这是一种很酷的技术。

function menuReadyTracker(initialValue) {

    var value = initialValue;
    var subscribers = [];

    this.get = function () {
        return(value);
    };

    this.set = function(newVal) {
        if (newVal != value) {
            value = newVal;
            var o;
            for (var i = 0; i < subscribers.length; i++) {
                o = subscribers[i];
                o.func.call(o.ctx, o.data);
            }
        }
    }

    // call subscribe to register a notification callback
    this.subscribe = function(f, data, ctx) {
        var o = {};
        o.func = f;
        o.data = data;
        o.ctx = ctx || window;
        subscribers.push(o);
    }

    // call unsubscribe to remove a notification callback
    this.unsubscribe = function(f, data, ctx) {
        var o;
        var newSubscribers = [];
        for (var i = 0; i < subscribers.length; i++) {
            o = subscribers[i];
            if (o.func != f || o.data != data || o.ctx != ctx) {
                newSubscribers.push(o);   // copy if it doesn't match
            }
        }
        subscribers = newSubscribers;
    }
}

var menuReady = new menuReadyTracker(false);
menuReady.subscribe(myNotification, "in real-time.");

console.log(menuReady.get());

menuReady.set(true);
console.log(menuReady.get());

// this function gets called whenever the menu flag goes from false to true
// you can have as many subscribers to this notification as you want and 
// they code that handles the notifications can be anywhere you want in your
// code base as the notification handlers are registered with the subscribe method
function myNotification(data) {
    alert("I got notified " + data);
}

jsFiddle:http://jsfiddle.net/jfriend00/PYJef/

答案 3 :(得分:2)

由于这是一个非常老的问题,而且许多答案都已过时,我想我应该添加一个最新的答案。

背景

JavaScript是事件驱动的语言。因此,除了第一次加载页面时,环境中任何时候发生的任何更改都是由某种事件引起的。因此,传达变更的最佳方法是使用事件。您可以收听引起您要观看的更改的同一事件,也可以创建一个新事件以使人们知道特定更改的时间。

Node.js具有一个eventEmitter对象,该对象非常有用并且是纯Javascript(不涉及DOM)。如果人们想要一种独立于DOM的通用事件机制,也可以在浏览器中使用相同的代码。

浏览器DOM具有其自己的事件通知系统,您可以在其中创建事件,触发事件和侦听事件。

轮询更改(如此处接受的答案所示)通常是一种“ hack”,效率低下,可能错过更改,不一定及时,浪费电池和CPU等。如果您控制代码,则可以总是比轮询更好的设计方式。

OP的实际问题

仍然没有一种有效的机制来监视变量更改,但是OP的问题实际上是有人想知道何时触发此代码:

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

当然,一个简单的答案是,任何想知道何时准备就绪的人也可以使用$(...).load()来监视同样的事情,但是这会使他们的通知与具体实现方式联系在一起。

创建任何人都可以收听的我们自己的事件通知

相反,最好是创建自己的事件,并且当我们知道菜单已准备就绪时,我们将触发该事件。这样,代码中任何地方的任何人都可以侦听同一事件。

浏览器已经内置了here中所述的可扩展事件系统。因此,我们可以在这里使用它。您需要选择一个众所周知的对象来触发事件,该事件在任何侦听器想要注册以了解此事件时就已经存在。由于OP没有显示任何特定的上下文,因此我只选择window对象。

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

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

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

请注意,您可以使用任何DOM对象作为事件的源(对您的应用程序最有意义的对象),只要它在有人要注册该事件的时间以及该事件的时间都存在即可发生。

然后,代码中任何地方的侦听器都可以侦听此事件:

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

工作演示

这是一个实际可行的实现,可以在按下按钮时触发事件。

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>


我精心设计了此答案以匹配此处问题中说明的原始问题。作为参考,还有其他一些有用的更改机制,但它们通常依赖于对象的属性,而不仅仅是简单的变量。

查看有关此问题的所有答案:Listening for variable changes。但要特别确保您阅读以下两个内容:

Using a proxy object to track changes

Using getters and setters to track changes

答案 4 :(得分:1)

一种方法是持续投票:

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

和...

<body onload="checkMenu();">

答案 5 :(得分:1)

function menuReady(){
  // Do whatever it is you need done when menu is ready
}    

$(...).load(..., function() {
  menuReady();// menuReady is called on callback    
});

答案 6 :(得分:0)

为什么不创建一个功能menuReady会在你想要的时候触发?

menuReady();

答案 7 :(得分: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]);}
                    });
            }