Gleb
Gleb

Reputation: 1432

One event handler for multiple selects

I have multiple selects on a page, like this:

<select id="select-1" class="action">
  <option val=1>1</option>
  <option val=2>2</option>
</select>
<select id="select-2">
  <option val=1>1</option>
  <option val=2>2</option>
</select>
<select id="select-3" class="action">
  <option val=1>1</option>
  <option val=2>2</option>
</select>

I need to do some actions with JS code when selects with class "action" are changed.

I could do something like this:

var elements = document.getElementsByClassName("classname");
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', myFunction, false);
}

But maybe there is a better way to handle this.

So my question is: what is the best way to use one event handler to handle multiple select elements?

Upvotes: 0

Views: 952

Answers (2)

Mister Jojo
Mister Jojo

Reputation: 22320

First of all : your html is invalid : options element has value attribute , no val, and this value must have quotes around.

so <option val=1>1</option> is wrong,
and must be <option value="1">1</option>

for event delegation, you need to have a parent element.

As your code are about select elements, the logic parent should be a <form> element.

and for the selects, the main event is a change event, not a click event

last things, all form element must have a name, this name is used when the form is submited , and not the id.

the other interest to use name attribute in forms, is they can be directly used to reference any sub element of the form

so the complete code is:

const myForm = document.forms['my-form']

// get any -change- event on all the form
myForm.addEventListener('change', myFunction, false) 

// event delegation function
function myFunction(evt)
  {
   // ignore other cases:
  if (!evt.target.matches('select.action')) return

  console.clear()
  console.log( 'change on :', evt.target.name )
  console.log( 'value is', myForm[evt.target.name].value )
  }
  
// disable form submit
myForm.onsubmit =evt=>evt.preventDefault()  
<form action="" name="my-form">
  <select name="select-1" class="action">
    <option value="s1_1"> s1 -1 </option>
    <option value="s1_2"> s1 -2 </option>
  </select>
  <select name="select-2">
    <option value="s2_11"> s2 -1 </option>
    <option value="s2_12"> s2 -2 </option>
  </select>
  <select name="select-3" class="action">
    <option value="s3_11"> s3 -1 </option>
    <option value="s3_12"> s3 -2 </option>
  </select>
</form>

Upvotes: 2

T.J. Crowder
T.J. Crowder

Reputation: 1074385

Another way is to use event delegation, where you hook click on an ancestor element that all of these selects are in, and take advantage of the fact that click bubbles:

document.querySelector("selector-for-the-container").addEventListener("click", function(event) {
    const select = event.target.closest(".action");
    if (select && this.contains(select)) {
        event.currentTarget = select;
        return myFunction.call(select, event);
    }
});

The update of currentTarget and calling myFunction with this set to the select make it look like the event was directly attached to the select. You don't have to do that, though, or you could do just one but not the other, it depends on what you want myFunction to do.

See on MDN:

Upvotes: 2

Related Questions