Reputation: 33
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?
Upvotes: 2
Views: 145
Reputation: 18901
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. e.g. 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>
Upvotes: 1
Reputation: 3032
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
Upvotes: 1
Reputation: 11080
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;
};
}
Upvotes: 0
Reputation: 16184
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>
Upvotes: 0
Reputation: 444
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.
Upvotes: -1