问题描述
我正在使用闭包来确保仅对绑定到元素的许多事件之一起作用,例如:$('.textInputs').on('keyup.my.evt change.my.evt',once(options));
我在_.debounce
函数闭包内使用下划线的once
。如果我在一个输入中进行更改,然后在另一个输入中进行更改,则一切正常。如果我在一个输入上进行更改,然后快速切换到下一个并在第二个输入上的时间等待到期之前的一秒钟内进行更改,那么反跳功能将仅对第二个输入执行。这告诉我,两个输入都使用了从去抖动返回的相同功能,但是我不确定如何更改代码结构,以便每个输入都使用自己的去抖动功能。
已反跳功能:
var doStuff = function(eleObj,options){ console.log(eleObj,options); } // the function that does stuff
var debouncedDoStuff = _debounce(doStuff,2000); // debouncing doStuff()
一次闭合定义:
var once = function (options) {
var eventType = null;
function doOnce(e) {
if (!eventType) {
eventType = e.type; // only the first eventType we get will be registered
}
if (e.type == eventType) {
debouncedDoStuff($(this),options); // calling the debounced do stuff func
}
}
return doOnce;
}
准备好文档:
$(document).ready(function(){
var options = {foo:'bar',baz:'biz'};
$('.textInputs').on('keyup.my.evt change.my.evt',once(options));
});
我做了一个小提琴来演示这个问题: https://jsfiddle.net/enzt7q90/
澄清说明 上面的代码已降至显示问题的最低限度。实际目标是将doStuff附加到ajax进程中,以保存对表格数据所做的任何更改。
我还应该指出,一次关闭与jQuery的.one()不同。例如,当按下tab键时,.one('keyup change',function(){})将执行func两次(一次用于keyup,再一次用于更改),而我使用一次闭包的功能仅对这两个事件执行一次。
解决方法
您可以通过将doStuff
传递到_.debounce
来创建防反射版本。您的问题是只能执行一次,因此只有一个这样的去反跳版本,事件处理程序必须在所有元素之间共享。去抖动意味着实际上只有一些调用可以到达原始功能。本质上,您将隐藏除doStuff
中的最近呼叫以外的所有呼叫。
给我的印象是,当您实际触发doStuff
时,您要考虑所有具有更改的输入元素。无论这些元素触发事件处理程序多少次,您都只希望处理一次。这意味着应该像您一样对处理(doStuff
)进行去抖动处理,但是立即事件处理程序(doOnce
)必须以某种方式区分输入元素。
Boudy hesham已经commented已经提出了一种可能的解决方案,即为每个输入注册一个单独的事件处理程序,并为其单独使用一个doStuff
的防反跳版本:
var doStuff = function(eleObj,options){ console.log(eleObj,options); } // same as before
function once(options) {
// Note that the debounced function is now generated inside the
// event handler factory.
var debouncedDoStuff = _.debounce(doStuff,2000);
function doOnce(e) {
// We don't need to restrict the event type anymore,because
// this event handler is specific to a single input element.
// The debounce already ensures that `doStuff` is called only once
// for this element.
debouncedDoStuff($(this),options);
}
return doOnce;
}
$(document).ready(function(){
var options = {foo:'bar',baz:'biz'};
// Using jQuery's .each to generate an event handler for each input
// element individually.
$('.textInputs').each(function() {
$(this).on('keyup.my.evt change.my.evt',once(options));
});
});
另一种选择是保留所有触发事件的输入元素的列表,并在超时后最终运行doStuff
时对它们进行重复数据删除:
var doStuff = function(elementList,options){
var uniqueElements = _.uniq(elementList);
console.log(uniqueElements,options);
}
var debouncedDoStuff = _debounce(doStuff,2000); // as original.
function once(options) {
var targetList = [];
function doOnce() {
targetList.push($(this));
debouncedDoStuff(targetList,options);
// Note that again,I don't restrict the callback to a single
// event type,because the debouncing and the deduplication
// already ensure that each input is processed only once.
// This also prevents a problem that might ignore a 'keyup' event
// in one element if 'change' was already triggered in another.
}
return doOnce;
}
// The next part is the same as your original code.
$(document).ready(function(){
var options = {foo:'bar',baz:'biz'};
$('.textInputs').on('keyup.my.evt change.my.evt',once(options));
});
后一种选择可能更精简一些,因为闭包较少,但是除非您有数百个输入元素,否则这可能并不重要。
第三个选择是使用框架以常规方式构造事件处理。 Backbone是与jQuery和Underscore完美配合的框架示例,可以很好地处理这种情况。但是,方便使用框架不在此答案的范围内。