-1

Basicly if I hover over a list item, I want to add a class to the corresponding span. Now I've found how to do this with the following code.

My question: Is there a way to simplify this (without repeating)? If so, how exactly?

Edit

My first ever post here. Figured only giving this js would be sufficient. So here is some more information.

This is about a navigation bar, which contains 4 list items. In every list item there is a span. If I hover over a particular listitem a border would apear on the corresponding span.

An eventListener for the whole page seems a bit rough, just want it for those 4 items.

  var listItems = document.querySelectorAll(".hover");
  var spanClass = document.querySelectorAll(".navbar-top-border");


  listItems[0].addEventListener("mouseover", event => {
    spanClass[0].classList.add("navbar-top-border-visible");
  });
  listItems[0].addEventListener("mouseout", event => {
    spanClass[0].classList.remove("navbar-top-border-visible");
  });

  listItems[1].addEventListener("mouseover", event => {
    spanClass[1].classList.add("navbar-top-border-visible");
  });
  listItems[1].addEventListener("mouseout", event => {
    spanClass[1].classList.remove("navbar-top-border-visible");
  });
  • You need to show HTML, so we can see the structure and see if there is a reason you only use 0 and 1 indexes, and if there is more of elements in those lists of elements. Only answers you will get without it are guess. Create [mre] – ikiK Feb 16 '21 at 13:39
  • @ikiK Generally, I would agree with you, but in this case, the rest of the HTML is irrelevant as it doesn't change the fundamental question or the solution (which does not require a guess to arrive at). – Scott Marcus Feb 16 '21 at 13:40

3 Answers3

1

Yes. Instead of biding each element to essentially the same event listeners, use "event delegation" where you bind the handler(s) to a common ancestor of the elements that need to use the callbacks. The event will originate at some element and then bubble up to the ancestor where it is handled. When it's handled, you can determine where it originated with event.target and then act accordingly.

Then, in your handler, if you need to access another element, use a DOM property to find that element in relation to the event.target (there are many possibilities to do this: closest, nextElementSibling, previousElementSibling, parent, etc.). Or, in your case, you can dynamically get the index of the moused over list item and act upon the span with that same index.

This way, you only set up handlers one time, which is less coding and less memory used by the various elements and no loops or hard-coded indexes are needed. It's also highly scalable as adding/removing DOM elements (either manually or dynamically) won't require any changes to the handler configurations.

Also, don't use .getElementsByClassName(), especially in connection with loops.

Here's an example:

// These collections will be used later to match up indexes
// but no looping or hard coding of indexes will be required.
var listItems = Array.from(document.querySelectorAll(".hover"));
var spanClass = document.querySelectorAll(".navbar-top-border");

// set up the event handler on a common ancestor
document.addEventListener("mouseover", foo1);
document.addEventListener("mouseout", foo2);

function foo1(event){
  // Test whether the event originated at
  // an element you care about
  if(event.target.classList.contains("hover")){
     // Find the span with the same index as the list item
     // and add the desired class
     spanClass[listItems.indexOf(event.target)].classList.add("navbar-top-border-visible");
  }
}

function foo2(event){
  // Test whether the event originated at
  // an element you care about
  if(event.target.classList.contains("hover")){   
     // Find the span with the same index as the list item
     // and remove the desired class
     spanClass[listItems.indexOf(event.target)].classList.remove("navbar-top-border-visible");
  }
}
.hover { color:blue; text-decoration:underline; cursor:pointer; }
.navbar-top-border { display:none; }
.navbar-top-border-visible { display:inline; }
<ul>
  <li class="hover">Item</li>
  <li class="hover">Item</li>
  <li class="hover">Item</li>
  <li class="hover">Item</li>
</ul>


<span class="navbar-top-border">Item 1</span>
<span class="navbar-top-border">Item 2</span>
<span class="navbar-top-border">Item 3</span>
<span class="navbar-top-border">Item 4</span>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
0

And how but this in case you really need only 0 and 1 as indexes.

var listItems = document.querySelectorAll(".hover");
var spanClass = document.querySelectorAll(".navbar-top-border");

let indxeses = [0, 1]

indxeses.forEach(el => {
  listItems[el].addEventListener("mouseover", event => {
    spanClass[el].classList.add("navbar-top-border-visible");
  });
  listItems[el].addEventListener("mouseout", event => {
    spanClass[el].classList.remove("navbar-top-border-visible");
  });
})
ikiK
  • 6,328
  • 4
  • 20
  • 40
  • 1
    *witch rest of the answers don't cover* <-- Well, my answer shows how to avoid indexes and loops entirely. ;) – Scott Marcus Feb 16 '21 at 13:48
  • @ScottMarcus Yeah I like the approach, but you essentially added event to all elements in document. And it still does not cover the case if there is 3 foo and he only wants to react to first 2... Like I said in comment of question, not enough info to answer 100 % correctly, or my answer did? :) – ikiK Feb 16 '21 at 13:53
  • *you essentially added event to all elements in document* <-- No, it's exactly the opposite of that. Handlers are only attached to one ancestor element, so only one object gets bloated up with additional configuration. Also, I showed adding them to `document`, but they can be added lower in the hierarchy as long as a common ancestor is used. And, if only some of the `foo` elements need to be dealt with, you simply write a more specific test in the handler. This really is the best approach. It also handled dynamic content that might be added/removed later. – Scott Marcus Feb 16 '21 at 13:57
  • Also, the benefit of no loops and no indexes makes the approach much more performant and scalable. – Scott Marcus Feb 16 '21 at 13:59
  • " Handlers are only attached to one ancestor element, so only one object gets bloated up with additional configuration." Thanks for this, I never really dinged in into this, I usually just loop and add events. In jquery I do delegate with document.on(event , foo)... – ikiK Feb 16 '21 at 14:00
  • Yeah, event delegation really is great for a lot of reasons. Events are going to bubble no matter what (unless `event.stopPropagation()` got used on a specific element for a specific event), so there's no extra overhead to using it and you actually reduce the overhead quite a bit by only assigning handlers to one object and you avoid loops and extra queries to create collections as well. – Scott Marcus Feb 16 '21 at 14:24
-1
var listItems = document.querySelectorAll(".hover");
var spanClass = document.querySelectorAll(".navbar-top-border");

listItems.map(function(element) {
    element.addEventListener("mouseover", event => {
        spanClass.map(function(spanElement) {
            spanElement.classList.add("navbar-top-border-visible");
        });
    });

    element.addEventListener("mouseout", event => {
        spanClass.map(function(spanElement) {
            spanElement.classList.remove("navbar-top-border-visible");
        });

    });
});

You can loop through the items instead of using item indexes.

Atul Sharma
  • 9,397
  • 10
  • 38
  • 65
  • And what if he only wants those indexes and not rest of the items if they are there? You are mapping everything. You dont see HTML... – ikiK Feb 16 '21 at 13:33
  • 1
    As per the code, it seems he is adding listener to all elements. – Atul Sharma Feb 16 '21 at 13:34
  • NO he is not, hes adding it to 2 indexes number, 0 and 1 for boths spanClass and listItems, what if there is more? You map will pick it up. – ikiK Feb 16 '21 at 13:36
  • Just use a `forEach` or `for` loop if your not mapping anything inside a `map` method – Reyno Feb 16 '21 at 13:40