user23139
user23139

Reputation: 368

Array vs If performance inside indexed for loop?

Which one has better performance? If statements are more readable but what if I make values in array (database-esque) and make my for loop call the function based on the current index which make it easier for further editing.

var msg = [
  "You are now viewing Tab number one",
  "You are now viewing Tab number two",
  "You are now viewing Tab number three",
  "You are now viewing Tab number four"
]

var button = document.getElementsByTagName("button")

for (var i = 0, len = button.length; i < len; ++i) {
  (function(index) {
    button[i].addEventListener("click",
      function() {
        document.getElementById("msg").innerHTML = msg[index];
      });
  })(i);
}
<button>Tab1</button>
<button>Tab2</button>
<button>Tab3</button>
<button>Tab4</button>
<br>
<p id="msg">Message here</p>

var button = document.getElementsByTagName("button")

for (var i = 0, len = button.length; i < len; ++i) {
  (function(index) {
    button[i].addEventListener("click",
      function() {
      if (index==0){
        document.getElementById("msg").innerHTML = "You are now viewing Tab number one";
      }
      if (index==1) {
        document.getElementById("msg").innerHTML = "You are now viewing Tab number two";
      }
      if (index==2) {
        document.getElementById("msg").innerHTML = "You are now viewing Tab number three";
      }
      if (index==3) {
        document.getElementById("msg").innerHTML = "You are now viewing Tab number four";
      }
      });
  })(i);
}
<button>Tab1</button>
<button>Tab2</button>
<button>Tab3</button>
<button>Tab4</button>
<br>
<p id="msg">Message here</p>

Upvotes: 0

Views: 557

Answers (3)

user2560539
user2560539

Reputation:

Using tabindex or data-index attribute might be a good idea in this occasion. The example snippet does not involve a loop and uses data-index.

var msg = [
  "You are now viewing Tab number one",
  "You are now viewing Tab number two",
  "You are now viewing Tab number three",
  "You are now viewing Tab number four"
]

document.body.addEventListener('click', (e) => {
  e.preventDefault();
  e.stopPropagation();
  if (e.target.tagName == 'BUTTON') {
    document.getElementById('msg').innerHTML = msg[e.target.dataset['index']];
  }
});
<button data-index="0">Tab1</button>
<button data-index="1">Tab2</button>
<button data-index="2">Tab3</button>
<button data-index="3">Tab4</button>
<br>
<p id="msg">Message here</p>

EDIT

Try to avoid using tabindex if you are going to have a large number of tabs. The following part is taken from MDN

Avoid using tabindex values greater than 0. Doing so makes it difficult for people who rely on assistive technology to navigate and operate page content. Instead, write the document with the elements in a logical sequence.

Furthermore tabindex has a maximum value of 32767.

Upvotes: 1

Schwern
Schwern

Reputation: 164679

For 4 items, or even 40, performance is irrelevant: computers are very fast and the list is very short. What's more important is how hard it is to read and maintain.

Performance

But it's worth looking into some underlying performance principles, particularly when it comes to lists, because small lists have an annoying tendency to get large.

An array lookup like button[i] is constant time or O(1). That means no matter how large button gets the lookup will always take the same time.

Whereas a succession of if statements, or a switch, must try each one until it finds one which matches. Best case is i is always 0 and it must compare 1 time. Worst case is i is always 3 and it must compare 4 times. If i is evenly distributed then it will, on average, take 2 times. This is called linear time or O(n), the time of the lookup increases as the size of button increases. If button gets twice as large the time gets twice as long.

With an array lookup as button gets larger the performance will remain the same. With if statements as button gets larger the performance will get worse.

Readability

Right now, starting out, you will find linear code easier to read. You'll prefer to have everything in front of you on the page in the order it is executed. But as the code gets more complicated this rapidly becomes overwhelming and will lead to a lot of cutting and pasting code violating the DRY principle: Don't Repeat Yourself.

Experienced programmers find that it's better to push as many details off somewhere else so one can clearly see the important parts. The important part is that it generates click handlers for each button that adds a message from msg. What's unimportant is exactly what that message is.

You could make the code even simpler, and possibly save some memory, by extracting the functions from the loop.

var addMsgListener = function(element, message) {
  element.addEventListener("click",
    function() {
      document.getElementById("msg").innerHTML = message;
    }
  )
};

var buttonMessages = [
  "You are now viewing Tab number one",
  "You are now viewing Tab number two",
  "You are now viewing Tab number three",
  "You are now viewing Tab number four"
]

var button = document.getElementsByTagName("button")

for (var i = 0, len = button.length; i < len; ++i) {
  addMsgListener(button[i], buttonMessages[i]);
}

Please pardon my possibly awful Javascript.

Now it's very clear what that loop does, for each button it adds a message listener. If I care what a message listener is, I can choose to look at addMessageListener. Or I can skip it. This makes the code "skimmable"; one can read the code to get an understanding of what it does and only dive into the details as necessary. It is not a linear read, but that is something you will get used to and come to prefer.

Note that addMsgListener no longer refers to msg nor button. Those are chosen by the loop using it. It is now generic and can be used for other elements and messages.

More reusability will now reveal itself; do the event and id need to be hard coded? This process of changing the code without changing what it does is called refactoring.

Maintenance

Every time you want to change a message you have to change code. If you want to add a message, you have to add code and renumber. If you want to remove a message, you have to delete code and renumber. If you want to reorder messages, you have to renumber. This requires a developer for what is a UI change. And the process is error prone, what if you remove a message and forget to change the indexes?

By putting the messages into an array, you only need to change msg. If you want to add a message, you add a line to msg. If you want to remove a message, you remove a line from msg. If you want to reorder the messages, you reorder msg.

msg does not have to live in the code, it can go into a configuration file making it even easier for a non-coder to change.

Action at a Distance

This leads us to our final problem: msg needs to be the same length and order as there are buttons. Every time the HTML is changed, msg might need to be changed, and vice-versa. But the buttons and their messages are in separate locations. This is known as Action at a Distance: changing one part of the code is affected by something in a totally different part of the code.

To avoid this, you could give each button a class or id and index their messages by that. This at least means the buttons and messages don't depend on their exact order. This is useful if you're not sure which buttons will be present in the HTML, but want to have messages for all possible buttons.

var msg = {
  car: "🏎",
  truck: "🚚",
  train: "🚆",
  plane: "✈"
}

You can go the other way; generate the buttons, and their messages, in the Javascript. It still keeps all the information about each button together. This is useful if the buttons are dynamic and can change.

You can do as Thomas suggests and attach the messages to the buttons themselves. Then all the data about the buttons is together. If you add, remove, or reorder the buttons the messages still work. This allows one to work on just the HTML.

Upvotes: 2

Thomas
Thomas

Reputation: 12637

That's like asking which high-end graphics card should I use for my simple digital clock? The Performance difference is neglectable. Using an instead of let has already a bigger performance impact than your actual question, array index vs. multiple ifs.

And it is still irrelevant.

About the Options, I find them both pretty hard to read, especially as the number of buttons gets bigger.

How about this?

document.querySelector(".buttons").addEventListener("click", function(event) {
  const button = event.target.closest("button[data-msg]");
  if (button) {
    document.querySelector("#msg").innerText = button.dataset.msg;
  }
})
<div class="buttons">
  <button type="button" data-msg="You are now viewing Tab number one">Tab1</button>
  <button type="button" data-msg="You are now viewing Tab number two">Tab2</button>
  <button type="button" data-msg="You are now viewing Tab number three">Tab3</button>
  <button type="button" data-msg="You are now viewing Tab number four">Tab4</button>
</div>
<p id="msg">Message here</p>

imo. nice and declarative. And easy to extend.

The condition if(button) deals with the possibility that you might add something else to div.buttons that could be clicked on, besides these buttons.

The .closest("button[data-msg]") delas with the cases where you might add more markup inside the buttons that might fire the event. We want the <button data-msg="...">

Upvotes: 4

Related Questions