如何防止事件监听器触发多个下拉菜单?

huangapple go评论73阅读模式
英文:

How to prevent Event Listener triggering for multiple drop down menus?

问题

我正在尝试使用后端Flask服务器通过触发AJAX请求来获取数据,当我与多选框下拉菜单交互时。基本上,我有两个下拉菜单,分别称为“选择日期”和“选择频道”。选择日期下拉菜单对应特定的.parquet文件,而“选择频道”对应于这些.parquet文件的列标题。

但是,当我与其中一个下拉菜单交互时,另一个下拉菜单的事件也会被触发(即使没有与它交互)。我应该如何避免这种情况?

以下是用于创建MultiCheckbox下拉菜单的JS脚本,我已将其保存为select-checkbox.js文件:

// 你的JS脚本代码

在HTML方面,我通过以下方式创建了两个下拉菜单,分别称为“选择频道”和“选择日期”:

<!-- 你的HTML代码 -->

下拉菜单的选项是基于后端Flask服务器生成的。

现在,我正在尝试使用事件监听器触发Ajax请求,以获取下拉菜单中所选选项的相应数据,例如,从“选择日期”下拉菜单中获取所选数据的频道数据。然而,当我与一个下拉菜单交互时,即使我没有与另一个下拉菜单交互,控制台也会打印出另一个下拉菜单的console.log。我认为这是因为改变事件监听器附加到整个文档上(document.addEventListener("change", ...))。这意味着每当文档中的任何地方发生任何变化事件(不仅仅是下拉菜单),事件监听器都会被触发。

你应该如何修复这个问题?

以下是用于fetchData函数和发出AJAX请求的JS脚本:

// 你的JS脚本代码

事件监听器如下:

// 你的JS脚本代码

如果你有任何问题或需要进一步的帮助,请告诉我。

英文:

I am trying to fetch data using a back end Flask server by triggering an AJAX request when I interact with the multi check box drop down menus. Essentially, I have two drop down menus called "Select Date" and "Select Channel". The Select Date dopdown corresponds to a particular .parquet file and "Select Channel" corresponds to the column headers for those .parquet files.

However, when I interact with one of the dropdown menus, the event for the other dropdown menu also gets triggered(wuthout even interacting with it). How do I avoid that?

I have the following JS script for creating MultiCheckbox dropdown menus which I have saved as select-checkbox.js:

        $(document).ready(function () {
            $(document).on(&quot;click&quot;, &quot;.MultiCheckBox&quot;, function () {
                var detail = $(this).next();
                detail.show();
            });

            $(document).on(&quot;click&quot;, &quot;.MultiCheckBoxDetailHeader input&quot;, function (e) {
                e.stopPropagation();
                var hc = $(this).prop(&quot;checked&quot;);
                $(this).closest(&quot;.MultiCheckBoxDetail&quot;).find(&quot;.MultiCheckBoxDetailBody input&quot;).prop(&quot;checked&quot;, hc);
                $(this).closest(&quot;.MultiCheckBoxDetail&quot;).next().UpdateSelect();
            });

            $(document).on(&quot;click&quot;, &quot;.MultiCheckBoxDetailHeader&quot;, function (e) {
                var inp = $(this).find(&quot;input&quot;);
                var chk = inp.prop(&quot;checked&quot;);
                inp.prop(&quot;checked&quot;, !chk);
                $(this).closest(&quot;.MultiCheckBoxDetail&quot;).find(&quot;.MultiCheckBoxDetailBody input&quot;).prop(&quot;checked&quot;, !chk);
                $(this).closest(&quot;.MultiCheckBoxDetail&quot;).next().UpdateSelect();
            });

            $(document).on(&quot;click&quot;, &quot;.MultiCheckBoxDetail .cont input&quot;, function (e) {
                e.stopPropagation();
                $(this).closest(&quot;.MultiCheckBoxDetail&quot;).next().UpdateSelect();

                var val = ($(&quot;.MultiCheckBoxDetailBody input:checked&quot;).length == $(&quot;.MultiCheckBoxDetailBody input&quot;).length)
                $(&quot;.MultiCheckBoxDetailHeader input&quot;).prop(&quot;checked&quot;, val);
            });

            $(document).on(&quot;click&quot;, &quot;.MultiCheckBoxDetail .cont&quot;, function (e) {
                var inp = $(this).find(&quot;input&quot;);
                var chk = inp.prop(&quot;checked&quot;);
                inp.prop(&quot;checked&quot;, !chk);

                var multiCheckBoxDetail = $(this).closest(&quot;.MultiCheckBoxDetail&quot;);
                var multiCheckBoxDetailBody = $(this).closest(&quot;.MultiCheckBoxDetailBody&quot;);
                multiCheckBoxDetail.next().UpdateSelect();

                var val = ($(&quot;.MultiCheckBoxDetailBody input:checked&quot;).length == $(&quot;.MultiCheckBoxDetailBody input&quot;).length)
                $(&quot;.MultiCheckBoxDetailHeader input&quot;).prop(&quot;checked&quot;, val);
            });

            $(document).mouseup(function (e) {
                var container = $(&quot;.MultiCheckBoxDetail&quot;);
                if (!container.is(e.target) &amp;&amp; container.has(e.target).length === 0) {
                    container.hide();
                }
            });
        });

        var defaultMultiCheckBoxOption = { width: &#39;220px&#39;, defaultText: &#39;Select Below&#39;, height: &#39;200px&#39; };

        jQuery.fn.extend({
            CreateMultiCheckBox: function (options) {
                    

                var localOption = {};
                localOption.width = (options != null &amp;&amp; options.width != null &amp;&amp; options.width != undefined) ? options.width : defaultMultiCheckBoxOption.width;
                localOption.defaultText = (options != null &amp;&amp; options.defaultText != null &amp;&amp; options.defaultText != undefined) ? options.defaultText : defaultMultiCheckBoxOption.defaultText;
                localOption.height = (options != null &amp;&amp; options.height != null &amp;&amp; options.height != undefined) ? options.height : defaultMultiCheckBoxOption.height;
                
                this.hide();
                this.attr(&quot;multiple&quot;, &quot;multiple&quot;);
                var divSel = $(&quot;&lt;div class=&#39;MultiCheckBox&#39;&gt;&quot; + localOption.defaultText + &quot;&lt;span class=&#39;k-icon k-i-arrow-60-down&#39;&gt;&lt;svg aria-hidden=&#39;true&#39; focusable=&#39;false&#39; data-prefix=&#39;fas&#39; data-icon=&#39;sort-down&#39; role=&#39;img&#39; xmlns=&#39;http://www.w3.org/2000/svg&#39; viewBox=&#39;0 0 320 512&#39; class=&#39;svg-inline--fa fa-sort-down fa-w-10 fa-2x&#39;&gt;&lt;path fill=&#39;currentColor&#39; d=&#39;M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41z&#39; class=&#39;&#39;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/div&gt;&quot;).insertBefore(this);
                divSel.css({ &quot;width&quot;: localOption.width });

                var detail = $(&quot;&lt;div class=&#39;MultiCheckBoxDetail&#39;&gt;&lt;div class=&#39;MultiCheckBoxDetailHeader&#39;&gt;&lt;input type=&#39;checkbox&#39; class=&#39;mulinput&#39; value=&#39;-1982&#39; /&gt;&lt;div&gt;Select All&lt;/div&gt;&lt;/div&gt;&lt;div class=&#39;MultiCheckBoxDetailBody&#39;&gt;&lt;/div&gt;&lt;/div&gt;&quot;).insertAfter(divSel);
                detail.css({ &quot;width&quot;: parseInt(options.width) + 10, &quot;max-height&quot;: localOption.height });
                var multiCheckBoxDetailBody = detail.find(&quot;.MultiCheckBoxDetailBody&quot;);


                this.find(&quot;option&quot;).each(function () {
                    var val = $(this).attr(&quot;value&quot;);

                    if (val == undefined)
                        val = &#39;&#39;;

                    multiCheckBoxDetailBody.append(&quot;&lt;div class=&#39;cont&#39;&gt;&lt;div&gt;&lt;input type=&#39;checkbox&#39; class=&#39;mulinput&#39; value=&#39;&quot; + val + &quot;&#39; /&gt;&lt;/div&gt;&lt;div&gt;&quot; + $(this).text() + &quot;&lt;/div&gt;&lt;/div&gt;&quot;);
                });

                multiCheckBoxDetailBody.css(&quot;max-height&quot;, (parseInt($(&quot;.MultiCheckBoxDetail&quot;).css(&quot;max-height&quot;)) - 28) + &quot;px&quot;);

            },
            UpdateSelect: function () {
                var arr = [];

                this.prev().find(&quot;.mulinput:checked&quot;).each(function () {
                    arr.push($(this).val());
                });

                this.val(arr);
            },
        });`

On the HTML side, I created two dropdown menus called "Select Channel" and "Select Date" in the following way:

&lt;head&gt;
    &lt;script&gt;
        $(document).ready(function () {
            $(&quot;#test&quot;).CreateMultiCheckBox({ width: &#39;230px&#39;, defaultText : &#39;Select Below&#39;, height:&#39;250px&#39; });
        });
    &lt;/script&gt;
        &lt;script&gt;
        $(document).ready(function () {
            $(&quot;#test_2&quot;).CreateMultiCheckBox({ width: &#39;230px&#39;, defaultText : &#39;Select Below&#39;, height:&#39;250px&#39; });
        });
    &lt;/script&gt;
&lt;/head&gt;`
  &lt;body&gt;
  
    &lt;div class=&quot;container&quot;&gt;
      &lt;div class=&quot;side-container&quot;&gt;
        &lt;!-- First Dropdown --&gt;
      &lt;div class=&quot;mb-3&quot; data-dropdown-type=&quot;channel&quot;&gt;
    &lt;label for=&quot;exampleFormControlSelect1&quot; class=&quot;form-label&quot;&gt;Select Channel&lt;/label&gt;
    &lt;select id=&quot;test&quot;&gt;
      {% for column in data %}
      &lt;option value=&quot;{{ column }}&quot;&gt;{{ column }}&lt;/option&gt;
    {% endfor %}

    &lt;/select&gt;

  &lt;/div&gt;
  &lt;!-- Second Dropdown --&gt;

  &lt;div class=&quot;mb-3&quot; data-dropdown-type=&quot;date&quot;&gt;
    &lt;label for=&quot;exampleFormControlSelect1&quot; class=&quot;form-label&quot;&gt;Select Date&lt;/label&gt;
    &lt;select id=&quot;test_2&quot;&gt;
      {% for parquet_file in parquet_files %}
      &lt;option value=&quot;{{ parquet_file }}&quot;&gt;{{ parquet_file }}&lt;/option&gt;
    {% endfor %}
&lt;/select&gt;
  &lt;/div&gt;`

The options for the drop down menus have been generated based on a back end Flask server.
Now I am trying to trigger an Ajax request using event listeners to fetch the corresponding data for the selected option on the drop down menuie. I want to get the Channel data for the selected Data from the "Select Date" dropdown menu. However, when I interact with one of the drop down menus, I also get a console.log printed for the other dropdown even without interacting with it. I'm assuming this is because the change event listener is attached to the entire document (document.addEventListener("change", ...)). This means that whenever any change event occurs anywhere in the document (not just on the dropdowns), the event listener is triggered.
How do I fix this?
Following is the JS script for the fetchData function and making the AJAX request:

 function fetchData(selectedChannels, selectedFiles) {
    console.log(&quot;Fetching data...&quot;);
    console.log(selectedChannels)
    console.log(selectedFiles)
    // Make an AJAX request to the backend with selectedChannel and selectedFile
    $.ajax({
      url: &quot;/fetch_data&quot;,
      type: &quot;POST&quot;,
      data: JSON.stringify({ channels: selectedChannels, files: selectedFiles }),
      contentType: &quot;application/json&quot;,
      success: function (response) {
        console.log(&quot;Data received successfully:&quot;, response);
        // Render the data received from the backend on the webpage
        displayData(response);
      },
      error: function(xhr, status, error) {
        console.error(&quot;Error fetching data:&quot;, error);
      },
    });
  }


  // Function to display the data on the webpage
  function displayData(data) {
    // You can update the DOM elements with the data received
    // For example, create tables or charts to visualize the data
    console.log(data);
  }

And here is the event listener:

  document.addEventListener(&quot;change&quot;, function (event) {
      const target = event.target;
      console.log(&quot;Event triggered.&quot;)
      console.log(target)

      var array = []
      $(&quot;#test option&quot;).each(function(){
                        if($(this).is(&quot;:selected&quot;))
                        {array.push($(this).val());}  
                    }); 
      console.log(array)
      var array1 = []      
      $(&quot;#test_2 option&quot;).each(function(){
                        if($(this).is(&quot;:selected&quot;))
                        {array1.push($(this).val());}    
                    }); 
      console.log(array1)
      if (array.length &gt;=1 &amp;&amp; array1.length&gt;=1) {
        const selectedChannels = Array.from(array)//.map(checkbox =&gt; checkbox.value);
        console.log(selectedChannels)
        const selectedFiles = Array.from(array1)//.map(checkbox =&gt; checkbox.value);
        console.log(selectedFiles)
        fetchData(selectedChannels,selectedFiles);
        
      }
      else{
        console.log(&quot;No match found&quot;)
      }
    });

  &lt;/script&gt;

答案1

得分: 0

将事件监听器直接注册到选择元素。

&lt;script&gt;
	$(document).ready(function () {
		$(&quot;#test&quot;).CreateMultiCheckBox({ 
			defaultText: &#39;Select Below&#39;, 
			height:&#39;250px&#39;, 
			width: &#39;230px&#39; 
		});
		$(&quot;#test_2&quot;).CreateMultiCheckBox({ 
			defaultText: &#39;Select Below&#39;, 
			height:&#39;250px&#39;, 
			width: &#39;230px&#39; 
		});

		$(&#39;#test, #test_2&#39;).change(function(evt) {
			// 在这里放置您的监听器代码!
		});
	});
&lt;/script&gt;

可能是因为您正在使用UpdateSelect中的JavaScript更新选择元素,所以事件可能没有触发。在这种情况下,当选择元素更新时,手动触发事件。

const event = new Event(&#39;change&#39;);
selectElem.dispatchEvent(event);
英文:

Register the event listener directly to the select elements.

&lt;script&gt;
	$(document).ready(function () {
		$(&quot;#test&quot;).CreateMultiCheckBox({ 
			defaultText: &#39;Select Below&#39;, 
			height:&#39;250px&#39;, 
			width: &#39;230px&#39; 
		});
		$(&quot;#test_2&quot;).CreateMultiCheckBox({ 
			defaultText: &#39;Select Below&#39;, 
			height:&#39;250px&#39;, 
			width: &#39;230px&#39; 
		});

		$(&#39;#test, #test_2&#39;).change(function(evt) {
			// Your listener code here!
		});
	});
&lt;/script&gt;

It may be that the events are not fired because you are updating the select element via JavaScript within UpdateSelect. In this case fire the event manually when the select-element is updated.

const event = new Event(&#39;change&#39;);
selectElem.dispatchEvent(event);

huangapple
  • 本文由 发表于 2023年7月31日 22:45:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76804736.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定