简体   繁体   中英

How to add event listeners automatically?

I am working with a lot of inputs (100 - 1000). I need to create a function for each of them like this:

const range1 = document.getElementById("a1");
const range2 = document.getElementById("a2");

//label1 shows a value from range1 + 3
range1.oninput = function() {
    document.getElementById("label1").innerHTML = parseInt(this.value) + 3;
};
range2.oninput = function() {
    document.getElementById("label2").innerHTML = parseInt(this.value) + 3;
};

Can I automate this somehow?

The best way is to assign just one listener to their common parent

document.getElementById(commonParentId).oninput = e =>
  document.getElementById("label" + e.target.id.substring(1))
    .innerHTML = parseInt(e.target.value) + 3

When the same task can be done on an unknown or big number of elements, then event delegation is the best option IMHO.

What is event delegation?

In a nutshell you let a common ancestor handle the task that you would have otherwise assigned to individual elements. eg instead of binding a click listener to n elements, you assign it once to a common ancestor. ( Read more .)

Why is it better?

If you're not careful adding too many event listeners will eventually degrade the perceived performance of your app. It will feel sluggish to your users.

Here's what John Resig says about it:

Event delegation is an efficient way to watch for an event on a large number of elements.

Source: https://johnresig.com/apps/workshop/adv-talk/index2.html#3

Demo time!

Every 2s I will append the following elements into the DOM: (X is generated)

<div>
  <input type="range" id="inpX"/>
  <label for="inpX"><!-- SOME DYNAMIC VALUE --></label>
</div>

The <label> will be updated as soon as the user adjusts the range.

All with exactly one event listener!

 // set el content to given txt const write = (el, txt) => el.innerText = txt; // return the <label> for given id const label = id => document.querySelector(`label[for="${id}"]`); // one global event listener - event delegation FTW! document.body.addEventListener('input', ev => { const {id, value} = ev.target; write(label(id), parseInt(value) + 3); });
 <script> // This code is only for the demo // It is not part of the answer function append(n) { const div = document.createElement('div'); const id = `inp${n}`; div.innerHTML = ` <input type="range" id="${id}"/> <label for="${id}"></label> `; document.body.appendChild(div); } function start() { let n = Date.now(); append(n++); start.timer = setInterval(() => append(n++), 2000); } function stop() { clearInterval(start.timer); } </script> <button onclick="start()">START</button> <button onclick="stop()">STOP</button> <hr>

Here's a simple demo:

 const ranges = document.querySelectorAll("input[type=range]"); for (i = 0; i < ranges.length; ++i) { //for each input[type=range] ranges[i].oninput = oninput; //set on change oninput.call(ranges[i]); //set on load } function oninput() { this.parentElement.querySelector("span").innerHTML = parseInt(this.value) + 3; };
 <label><input type=range /><span></span><label><br /> <label><input type=range /><span></span><label>

You can easily just use arrays and loops:

let inputNum = 100;
let range = [];

for(let i = 1; i <= inputNum; i++) {
    range[i] = document.getElementById(`a${i}`);
    range[i].oninput = function() {
        document.getElementById(`label${i}`).innerHTML = parseInt(this.value) + 3;
    };
}

There are plenty of frameworks to automate such things. What can be done with plain html/js:

<label for='a1'></label>
<label for='a2'></label>
<input id='a1' class='range-input-output-to-label'/>
<input id='a2' class='range-input-output-to-label'/>
<script>
  const inputs = document.getElementsByClassName('number-input-output-to-label')

  const outputNumberToLabel = (e) => {
    const labels = e.target.labels
    for (let i = 0; i < labels.length; i++)
      labels[i].innerText = parseInt(e.target.value) || 0
  }

  for (let i = 0; i < inputs.length; i++)
    inputs[i].oninput = outputNumberToLabel
</script>

Here I'm using vanilla for loops because native element lists are not array and does not support forEach.

I'm suggesting agains modifying ids to find label because it might be tricky to debug it later.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM