How to avoid repeating "document.getElementById" calls in JS?

Viewed 449

Here is an exanple:

document.getElementById("field_a").style.color = "red";
document.getElementById("field_b").style.color = "red";
document.getElementById("field_c").style.color = "red";
document.getElementById("field_d").style.color = "red";
document.getElementById("field_a").style.backgroundColor = "white";
document.getElementById("field_b").style.backgroundColor = "white";
document.getElementById("field_c").style.backgroundColor = "white";
document.getElementById("field_d").style.backgroundColor = "white";

Is it possible to make the same with less lines of code? Thank you.

6 Answers

Assuming you're assigning the same colors to everything, why not iterate over a list of field names?

fields = ['field_a', 'field_b', ...]
fields.forEach(field => {
    const fieldEl = document.getElementById(field);
    fieldEl.style.color = "red";
    fieldEl.style.backgroundColor = "white";
});

Array.prototype.forEach()

One option would be to add a class to all these elements and use document.getElementsByClassName or document.querySelectorAll to retrieve all of them and apply the rules with a loop.

var elements = document.getElementsByClassName("foo");
// or = document.querySelectorAll(".foo");

for (var element of elements) {
  element.style.color = "red";
  element.style.backgroundColor = "white";
}

You can create a nodeList by document.querySelectorAll and Array.from()

const nodeList = document.querySelectorAll('.field');
let elements = Array.from(nodeList);

elements.map(x => x.style.color = 'red');
<p class="field">Lorem</p>
<p class="field">Lorem</p>
<p class="field">Lorem</p>

Use function. Read more about DRY and SRP

fields = ['a', 'b', ...]

fields.forEach(field => {
  changeColor(field, "red", "white");
})

function changeColor(id, color, bgColor)
{
  let field = document.getElementById(id);
  field.style.color = color;
  field.style.backgroundColor = bgColor;
}

var fields = document.querySelectorAll ("[data-js='field']"); will return all the elements that have an attribute data-js = "field"

then you can loop through these elements with fields.forEach and set the color and the background color of each one like the code below

The attribute data-field-type can be used to select a specific field. Ex: var field_a = document.querySelector("[data-field-type='a']");

(In the code only 3 fields were used)

var fields = document.querySelectorAll ("[data-js='field']");

fields.forEach (function (_field){
  _field.style.color = "red";
});
<div data-js="field" data-field-type="a">field a</div>
<div data-js="field" data-field-type="b">field b</div>
<div data-js="field" data-field-type="c">field c</div>

To directly answer your question as worded:

const $ = document.getElementById.bind(document);

Now these are equivalent, and you've saved 22 characters per call:

$('field_a') == document.getElementById("field_a");

To make the example code you've shared more concise though, you can do this:

const nodes = document.querySelectorAll('[id^=field_]');
const elems = [...nodes];
elems.forEach(({ style }) => style.color = 'red' || (style.backgroundColor = 'white'));
<span id=field_a>a</span>
<span id=field_b>b</span>
<span id=field_c>c</span>
<span id=field_d>d</span>

The selector [id^=field_] selects all elements whose id starts with field_. Unfortunately, we are returned a NodeList instead of an Array, so it doesn't have a .forEach method (I would appreciate someone explaining why it was designed this way).

Fortunately, converting most iterables (like a NodeList) into an instance of Array is easy with the spread operator:

[...nodeList]

Then we can use .forEach to loop through each of them.

In my example, I've used ({ style }) to get the .style property of each element, since it's the only object we want to mutate.

Related