N. Saunders
N. Saunders

Reputation: 27

Target all links in a website - JavaScript

I'm trying to target all of the links in the document. When any link is clicked I want the modal to show. The modal is currently set to display: none. I'm not getting any error messages in console but it still doesn't work. The script link is at the bottom of the html document also. What am I missing?

var a = document.getElementsByTagName('a');
    for (i = 0; i < a.length; i++) {   
        var links = a[i];
    } 

At this point, var links should equal all links, correct?

links.addEventListener('click', openmodal);

function openmodal(){
    modal.style.display = "block";
}

The code below did work but only for the very first link

var links = document.getElementsByTagName('a')[0];

Upvotes: 0

Views: 932

Answers (4)

Scott Marcus
Scott Marcus

Reputation: 65855

You have a few things to correct. First:

var a = document.getElementsByTagName('a');
for (i = 0; i < a.length; i++) {   
   var links = a[i];
} 

Because you are using var links inside the loop, you are re-declaring it upon each loop iteration and therefore losing any previous value it might have been storing. The correct thing to do would be to declare it outside of the loop and just use it (without var) inside the loop:

var a = document.getElementsByTagName('a');
var links; 
for (i = 0; i < a.length; i++) {   
   links = a[i];
} 

But, even then, because you are assigning it a value with =, you are still overwriting the last value stored in the variable with a new one upon each loop iteration. Now, with the code snippet above, it's not entirely clear what you were intending to do.

So, to accomplish your goal of setting each link up to activate the modal, we only need to gather up all the links, iterate over them and while doing so, set up an event handler:

var links = document.getElementsByTagName('a');   // Get all the links
for (i = 0; i < a.length; i++) {                  // Loop over them
   links[i].addEventListener('click', openmodal); // Assign handler to each
} 

But, be aware of a few things:

  • getElementsByTagName() returns a "live node list" and will always refer to the most up-to-date set of matching nodes. It works this way by re-querying the document every time you reference the node list. If you had 20 links in your document and ran the code above, it would wind up querying the DOM for all a elements 21 times!!! Live node lists are only beneficial when you are dynamically adding/removing elements from a document and always need the most up-to-date set of matching elements. But, as I've pointed out, you pay a performance penalty for using them. For that reason, and the fact that there are better ways to get the same result that don't incur that hit, live nodes lists should generally be avoided.
  • Anytime you use a loop, you open the door to performance issues. I'm not saying don't use loops, but I am saying that you should always be very aware of what you are doing in that loop. An inefficient operation or an operation that takes resources will be more inefficient or take more resources when done repeatedly. Assigning an event handler to many elements uses memory because each element needs to store a copy of that callback function.

The best approach is to use event delegation, which entails you setting up an event handler on a higher level element (like document) and because of event bubbling (which is a process that is going to happen by default anyway, so no additional performance is required), we can handle the event at that higher level. We then just test what the actual element was that triggered the event and act accordingly. This way, we don't have to loop and set up all kinds of event handlers on many elements (which requires resources).

// Set up one event listener at a high level that click events will bubble up to
document.addEventListener("click", testTarget);

function testTarget(event){

   // Test to see if the event was triggered by a click to an <a> element
   if(event.target.nodeName === "A"){
      // It was an <a> element that was clicked, show the modal
      showModal();
   }

}

Upvotes: 3

Unmitigated
Unmitigated

Reputation: 89412

You need to add the event listener to each anchor element separately inside the loop or you can use Array.prototype.forEach.

//normal for loop
var a = document.getElementsByTagName('a');
for (i = 0; i < a.length; i++) {   
        var link = a[i];
        link.addEventListener("click", openmodal);
}

//forEach
[].forEach.call(document.querySelectorAll('a'), function(anchor, index){
    anchor.addEventListener("click", openmodal);
});

Upvotes: 1

Daniel Rothig
Daniel Rothig

Reputation: 706

You have to add the event listener to each link separately:

var a = document.getElementsByTagName('a');
for (i = 0; i < a.length; i++) {   
    var links = a[i].addEventListener('click', openmodal);
} 

But note that if new links arrive in your document after that, this won't affect the new links so a more resilient version is:

document.addEventListener('click', function(event) {
  if (event.target.matches('a[href], a[href] *')) {
    event.preventDefault();
    openmodal();
  }
}, false);

Upvotes: 1

Ryan Cogswell
Ryan Cogswell

Reputation: 81096

You need to add the event listener in the loop.

function openmodal(){
    modal.style.display = "block";
}
var links = document.getElementsByTagName('a');
for (i = 0; i < links.length; i++) {   
   links[i].addEventListener('click', openmodal);
} 

Upvotes: 3

Related Questions