从最近的 .change() 事件中获取选中的复选框

Get checked checkboxes from most recent .change() event

我在解决一个基本问题时遇到了麻烦,但我什至无法弄清楚要帮助自己进行研究的正确术语是什么。

本质上,我有一个 .change() 侦听器,用于侦听 div 中复选框的变化(它们切换传单地图层)。我想创建一个变量来保存已选中复选框的列表。我可以做到这一点,但是,对于 .change() 侦听器每次触发,它似乎都会保留检查过哪些层的各个实例。一些应该重现问题的代码:

var lyrs;

$("div#myID input:checkbox").change(function() {
    lyrs = $("div#myID input[type='checkbox']:checked").map(function() { return this.id });
    console.log('in .change(): ', lyrs)
    legendFunction( lyrs );

});

function legendFunction( lyrs ) {
    map.on('click', function() {
        console.log('in legendFunction: ', lyrs);
    });
};

当我在 .change 回调函数中将变量打印到控制台时,存在单个 lyrs 对象。但是,当我从 legendFunction 中的 .on('click') 回调函数打印它时,它会打印一个图层列表(由于缺少更好的词),每次选中或取消选中复选框时都会检查这些图层。下图显示了这一点:

这是怎么回事?如何让 .on('click') 函数中的 lyrs 变量仅对应于最近的 .change() 事件的图层?

问题是每次单击复选框时都会通过 legendFunction 注册一个 Leaflet Event 回调。因此,每次您单击地图时,Leaflet 都会使用用于注册回调的 lyrs 的值执行回调。

您可能只想注册一个 click 事件处理程序。像这样:

HTML:

<div id="layer1" class="layers">
  <input type="checkbox" id="layer1" />
  <label>layer1</label>
</div>

<div id="layer1" class="layers">
  <input type="checkbox" id="layer2" />
  <label>layer2</label>
</div>

<div id="mapid"></div>

JavaScript:

var mymap = L.map('mapid').setView([51.505, -0.09], 8);

mymap.on('click', function() {
  $(".layers input[type='checkbox']:checked").each(function() {
    console.info(this.id, 'is checked');
  });
});

你的 legendFunction 向你的地图添加了一个事件处理程序(它没有显示,但我假设 legendFunction 中的 map 是一个代表你的 jQuery 对象实际地图)。问题是每次该函数运行时它都会添加一个新的事件处理程序。 From the jQuery documentation:

As of jQuery 1.4, the same event handler can be bound to an element multiple times.

并且所有这些重复的处理程序都将在触发该事件时触发。每次您单击一个复选框时,您的复选框更改处理程序都会触发,它会依次调用 legendFunction,这会向您的地图添加一个新的点击处理程序。

迭代 1

第一次通过时,lyrs 被设置为选中复选框的 jQuery 对象,它将列在控制台上。 map 上的点击还没有事件处理程序(除非 legendFunction() 在其他地方也被触发,或者如果您在其他地方也添加了另一个点击处理程序),所以 legendFunction (或者更确切地说legendFunction 添加的事件处理程序)这次不会生成任何控制台输出。此迭代添加第一个 map 单击事件处理程序。

迭代 2

下次单击复选框时,您将再次从复选框更改处理程序中将正确的 lyrs 记录到控制台。你没有描述它,但如果你的复选框实际上是 on 你的 map,那么同样的点击将从复选框向上冒泡到 map,并触发map click 处理程序也在迭代 1 中添加。根据您的 HTML 结构,lyrs 将已使用新的一组选中的复选框进行更新,并且迭代 1 的 map 单击处理程序的输出将反映这一点。此外,此点击调用 legendFunction(),并添加了第二个重复的 map 点击事件处理程序。

迭代 3

同上,除了现在触发了 2 个相同的 map 单击处理程序,每个处理程序都在控制台上生成输出。添加了第三个重复的 map 单击事件处理程序。

...等等

我不知道具体目标是什么,但要避免此问题,只需创建一次 map 单击事件处理程序:

map.on('click', function() {
    console.log('map was clicked, layers are', lyrs);
});

您多次重新绑定点击事件。尝试只输出 lyrs 的值,而不是在 legendFunction 中再次绑定点击。

var lyrs;

$("div#myID input:checkbox").change(function() {
lyrs = $("div#myID input[type='checkbox']:checked").map(function() { return this.id });
console.log('in .change(): ', lyrs)
legendFunction( lyrs );

});

function legendFunction( lyrs ) {
        console.log('in legendFunction: ', lyrs);
};
jquery的

on方法会在Event Queue中添加一个Event Listener,也就是说调用on方法会在Event Queue中添加5个Event Listener五个times.So,当click事件触发时,会调用5次Event Callback,在使用on方法绑定事件监听前,需要取消所有事件监听。

var lyrs;

$("div#myID input:checkbox").change(function() {
    lyrs = $("div#myID input[type='checkbox']:checked").map(function() { return this.id });
    console.log('in .change(): ', lyrs)
    legendFunction( lyrs );

});

function legendFunction( lyrs ) {
    map.off('click')
    map.on('click', function() {
        console.log('in legendFunction: ', lyrs);
    });
};

这是因为你每次调用legendFunction时都绑定了一个监听器到map的点击事件。这是因为每次调用该函数时,您都会将 lyrs 数组作为参数传递(这与您在脚本顶部定义的 lyrs 变量不同)然后进行绑定,所以您正在创建一个闭包,该闭包会在该时间以及您随后每次调用该函数时记住该数组的值。

你的程序基本上是这样做的:

您勾选了 id landfireEVT

的复选框
  • 在更改回调中,您将有 lyrs = ['landfireEVT']
  • 当您调用 legendFunction 时,您会向 map 元素添加一个新绑定,它将 "remember" lyrs = [landfireEVT]
  • 如果您单击 map 元素,刚刚绑定的事件将触发,您将在控制台中看到 lyrs = ['landfireEVT']
  • 的值

现在有人勾选了 id agc

的复选框
  • 在更改回调中你将有 lyrs = ['landfireEVT','agc']
  • 当您调用 legendFunction 时,您会向 map 元素添加一个新绑定,它将 "remember" lyrs = ['landfireEVT','agc']
    • 如果您点击 map 元素,将触发先前绑定到点击事件的第一个回调,您将在控制台中看到 lyrs = ['landfireEVT'],然后是第二个回调刚刚绑定到点击事件的将触发,您将看到 lyrs = ['landfireEVT','agc']
    • 的值

我只绑定一次事件并将更新后的复选框列表保存在 lyrs 变量中

var lyrs;
map.on('click', function() {
   //lyrs will always be the variable defined above anytime somebody clicks on the map element.
   console.log('in legendFunction: ', lyrs);
 });

$("div#myID input:checkbox").change(function() {
  lyrs = $("div#myID input[type='checkbox']:checked").map(function() { return this.id });
  //add here any other logic you need to notify somebody else that a checkbox changed.
});

您可以在此处阅读有关闭包和作用域的更多信息 https://community.risingstack.com/explaining-javascript-closure-scope-chain-examples/