优雅的方法,以防止启动后立即停止事件

时间:2011-12-16 15:38:10

标签: javascript jquery events callback

我正在编写一个jQuery插件,其中启动/停止插件的事件是可自定义的,因此可能同一事件可以启动和停止插件(例如,单击开始并单击停止)。

什么是优雅的方式,理想情况下不涉及超时或解除绑定和重新绑定侦听器(而不是太多“isPlaying”“isBeingStarted”标志等。)以确保调用正确的回调

3 个答案:

答案 0 :(得分:2)

注意:当我发布这个答案时,问题中有一个拼写错误,只要不涉及超时,看起来好像绑定/取消绑定就好了。)

我认为不需要超时,只需要绑定/取消绑定:

this.bind(startEvent, start);

function start() {
    $(this).unbind(startEvent).bind(stopEvent, stop);
}

function stop() {
    $(this).unbind(stopEvent).bind(startEvent, start);
}

在上面,我假设startEvent是配置的开始事件名称(我可能会为其添加一个命名空间,例如用户传递"click"但你添加".niftyplugin" }导致startEvent包含"click.niftyplugin",因此您可以随意绑定/取消绑定),stopEvent是配置的停止事件名称(带命名空间)。

这是一个完整的示例,包含名称空间并使用data来记住选项(如果您愿意,可以使用闭包) - live copy

// Plugin stuff
(function($) {
  $.fn.niftyPlugin = niftyPlugin;
  function niftyPlugin(options) {
    var data;

    data = {
      startEvent: (options && options.startEvent || "click") + ".niftyplugin",
      stopEvent: (options && options.stopEvent || "click") + ".niftyplugin"
    };

    this.data("niftyPlugin", data).bind(data.startEvent, start);

    return this;
  }

  function start() {
    var $this = $(this),
        data = $this.data("niftyPlugin");
    $this.unbind(data.startEvent).bind(data.stopEvent, stop);
    display("Start");
  }

  function stop() {
    var $this = $(this),
        data = $this.data("niftyPlugin");
    $this.unbind(data.stopEvent).bind(data.startEvent, start);
    display("Stop");
  }

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }
})(jQuery);

// Use
jQuery(function($) {
  $("#theButton").click(function() {
    $("<p>Non-plugin hook fired</p>").appendTo(document.body);
  }).niftyPlugin({
    startEvent: "click"
  });
});

我看到的唯一其他选择是stopImmediatePropagation - live example

// Plugin stuff
(function($) {
  $.fn.niftyPlugin = niftyPlugin;
  function niftyPlugin(options) {
    var startEvent, stopEvent, running = false;

    startEvent = (options && options.startEvent || "click") + ".niftyplugin";
    stopEvent = (options && options.stopEvent || "click") + ".niftyplugin";

    this.bind(startEvent, start).bind(stopEvent, stop);

    return this;

    function start(event) {
      if (running) {
        return;
      }
      running = true;
      display("Start");
      event.stopImmediatePropagation();
    }

    function stop(event) {
      if (!running) {
        return;
      }
      running = false;
      display("Stop");
      event.stopImmediatePropagation();
    }
  }

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }
})(jQuery);

// Use
jQuery(function($) {
  $("#theButton").click(function() {
    $("<p>Non-plugin hook fired</p>").appendTo(document.body);
  }).niftyPlugin({
    startEvent: "click"
  });
});

我不喜欢它,因为它会干扰事件的其他处理程序。例如,在上面,如果我将使用改为:

// Use
jQuery(function($) {
  $("#theButton").niftyPlugin({
    startEvent: "click"
  }).click(function() {
    $("<p>Non-plugin hook fired</p>").appendTo(document.body);
  });
});

...所以插件在非插件代码繁荣之前抓取事件,非插件代码永远不会看到事件(example

所以尽管有开销,但我怀疑bind / unbind是你的朋友。

答案 1 :(得分:0)

这可能是矫枉过正,但是不必保持一堆旗帜(例如&#34; isPlaying&#34;)的优雅方法是使用Finite State Machine

这是一个jQuery实现:https://github.com/DukeLeNoir/jquery-machine

答案 2 :(得分:0)

我选择的最终解决方案是对用于停止和启动的事件进行快速唯一性测试,如果有任何事件用于停止和启动,则附加不同的侦听器(执行isPlaying检查)对这些。加载插件时性能受到很小的影响,但之后事件处理代码的效率几乎可以达到。

function processEvents() {
    var tempStart = opts.startEvent.split(" ").sort(),
        tempStop = opts.stopEvent.split(" ").sort();
    startEventLoop: for(var i=0, il = tempStart.length;i<il;i++) {
        for(var j=0, jl = tempStop.length;j<jl;j++) {
            if(tempStart[i] == tempStop[j]) {
                stopStartEvents.push(tempStart[i])
                tempStop.splice(j,1);
                continue startEventLoop;
            }
        }
        startEvents.push(tempStart[i])
    }
    startEvents = startEvents.join(" ");
    stopEvents = tempStop.join(" ");
    stopStartEvents = stopStartEvents.join(" ");
}

    $this.on(stopEvents, function() {
        $this.trigger("stop.flickBook");
    }).on(startEvents, function() {
        $this.trigger("start.flickBook");
    }).on(stopStartEvents, function() {
        playing ? $this.trigger("stop.flickBook") : $this.trigger("start.flickBook");
    });