I am using Select2 for a multi select. This select is used to filter a list.
Selecting one item rebuilds the list with that filter(s). Removing one item, again rebuild the list without that specific item. You can select more than one items.
I have attached a behaviour on the "on change" event of the select. This is going well. The list is properly rebuild when individual items are selected/deselected.
My problem is when I click on the "clear handle", I get too many events. For example, when I have two items selected in the selector, if I click the clear button, I get 3 change
events and the list is rebuild 3 times! I would like to have only one event that I can rely on when clearing the selection.
I could use the clearing
or clear
event, but would I need a way to rebuild the list when individual items are selected or unselected. The problem is I cannot use both... a select2:unselect
/change
event is triggered for every individual items that is removed from the list whether you remove individual item manually or use the clear button that removes all items automatically...
Using the clear button should trigger the change
event only once!
The select2:unselect
could be triggered once per item removed though...
const $field = $("[name=color]");
$field.select2({
closeOnSelect : false,
placeholder: "Please select",
allowHtml: true,
allowClear: true,
tags: true
});
$field.on("change", function(e) {
console.log("pulldown change");
// Use $this.val() to rebuild the list with the new filters
});
$field.on("change.select2", function(e) {
console.log("change.select2");
});
$field.on("select2:select", function(e) {
console.log("select2:select");
});
$field.on("select2:unselect", function(e) {
console.log("select2:unselect");
});
$field.on("select2:clearing", function(e) {
console.log("select2:clearing");
});
$field.on("select2:clear", function(e) {
console.log("select2:clear");
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
<select class="form-control" style="width: 100%" name="color" multiple>
<option value="blue">blue</option>
<option value="red">red</option>
<option value="yellow">yellow</option>
</select>
Browser console after clicking the clear button. (The X on the input that replaces the select box)
select2:clearing
select2:clear
pulldown change
change.select2
select2:unselect
pulldown change
change.select2
select2:unselect
pulldown change
change.select2
In short, clicking on the clear button will trigger the clearing
and the clear
events, it will then trigger a change
, change.select2
and a select2:unselect
event for each items that was selected. It then triggers a change
and a change.select2
event once more.
You can wrap your update function in a debounce. You will need a flag to reset each time the function gets called, so it does not get spammed.
const debounce = (func, wait) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
};
function fetchChildList(selectedValues) {
// Logic to fetch/update the child-list based on selected values
console.log("Fetching child list for values:", JSON.stringify(selectedValues));
}
$(document).ready(function() {
const $field = $("[name=color]");
const debouncedFetchChildList = debounce(fetchChildList, 300);
let isClearing = false;
$field.select2({
closeOnSelect: false,
placeholder: "Please select color(s)",
allowHtml: true,
allowClear: true,
tags: true
});
$field.on("select2:clearing", function(e) {
console.log("select2:clearing");
isClearing = true;
});
$field.on("select2:clear", function(e) {
console.log("select2:clear");
// Set timeout to wait for the change events to complete
setTimeout(() => {
debouncedFetchChildList([]);
isClearing = false; // Reset the clearing flag after fetch
}, 0);
});
$field.on("change", function(e) {
if (isClearing) {
return; // Skip change event handling during clearing
}
const selectedValues = $field.val();
console.log("Selected values:", JSON.stringify(selectedValues));
debouncedFetchChildList(selectedValues);
});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
<select class="form-control" name="color" multiple="multiple" style="width: 100%">
<option value="blue" selected="true">Blue</option>
<option value="red" selected="true">Red</option>
<option value="yellow" selected="true">Yellow</option>
</select>
I wrapped all this logic into a plugin called $.fn.select3
:
Note: I used the $.debounce
function from the jquery-throttle-debounce
rather than my hand-rolled version.
function fetchChildList(selectedValues) {
console.log("Fetching child list for values:", JSON.stringify(selectedValues));
}
$(document).ready(function() {
$("[name=color]").select3(fetchChildList, {
closeOnSelect: false,
placeholder: "Please select color(s)",
allowHtml: true,
allowClear: true,
tags: true
});
});
(function($) {
$.fn.select3 = function(updateFn, options) {
const debouncedUpdateFn = $.debounce(300, updateFn);
let isClearing = false;
function onClearAll(e) {
isClearing = true;
}
function onClear(e) {
setTimeout(() => {
debouncedUpdateFn([]);
isClearing = false; // Reset the clearing flag after fetch
}, 0);
}
function onChange(e) {
if (isClearing) return;
debouncedUpdateFn($(e.target).val());
}
return this.select2(options)
.on({
'select2:clearing': onClearAll,
'select2:clear': onClear,
'change': onChange
});
};
})(jQuery);
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-throttle-debounce/1.1/jquery.ba-throttle-debounce.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
<select class="form-control" name="color" multiple="multiple" style="width:100%">
<option value="blue" selected="true">Blue</option>
<option value="red" selected="true">Red</option>
<option value="yellow" selected="true">Yellow</option>
</select>