Javascript encapsulation scope

The project I was working on today involved front-end Javascript. To summarize the issue and solution, I needed to add click handlers to links on a page that redirect users to other pages. I utilized two JavaScript arrays: arrayOfRedirectLinks and pageLinkElements:

var arrayOfRedirectLinks, pageLinkElements;

Initially, I wrote the addEventHandlers function as follows:

var addEventHandlers = function() {
    var i, link;
    for( var i in arrayOfRedirectLinks) {
        link = arrayOfRedirectLinks[i];
        pageLinkElements[i].addEventListener('click', function(e) {
            e.preventDefault();
            window.location = link;
        });
    }
}

However, upon testing this solution, I found that all links redirected me to the same destination (the last link in arrayOfRedirectLinks).

Eventually, I came across a similar problem and solution described here: Javascript multiple dynamic addEventListener created in for loop - passing parameters not working

The following revised solutions resolved my issue:

var addEventHandlers = function() {
    var i, link;
    for( var i in arrayOfRedirectLinks) {
        (function(link){
        link = arrayOfRedirectLinks[i];
        pageLinkElements[i].addEventListener('click', function(e) {
            e.preventDefault();
            window.location = link;
        });
        }(link));
    }
}

and

var passLink = function(link) {
    return  function(e) {
               e.preventDefault();
               window.location = link;
            };
};
var addEventHandlers = function() {
    var i, link;
    for( var i in arrayOfRedirectLinks) {
        link = arrayOfRedirectLinks[i];
        pageLinkElements[i].addEventListener('click',passLink(link));
    }
}

This updated approach solved the issue, though the exact reasoning behind its success remains unclear to me. My explanation is as follows:

  1. Functions in JavaScript reference variables from the scope they were declared in. This means my event handler obtains a reference to the link variable within the addEventHandlers function.

  2. Due to the referenced nature of the link variable, any updates made to it will affect all click handlers linked to it. This resulted in all links directing to the last value of link in the array arrayOfRedirectLinks.

  3. Passing the link variable as a parameter to another function creates a new scope where the link shares only its initial value with the passed parameter. Consequently, each event handler's link is isolated, maintaining the correct value from arrayOfRedirectLinks[i].

  4. In conclusion, passing the link to the click handler creates distinct references for each instance, preventing interference between them and preserving the correct redirection values.

Does this explanation accurately depict the situation?

Answer №1

This snippet contains a crucial element:

var index, reference;
for( var index in arrayOfRedirectLinks) {
  reference = arrayOfRedirectLinks[index];
  pageLinkElements[index].addEventListener('click', function(event) {
    event.preventDefault();
    window.location = reference;
  });
}

It is important to note that there is just one reference variable within this block of code. The callback function for the addEventListener method is executed only upon clicking the link.

At the time when the link is clicked, the reference variable has already been assigned its final value, which is then shared by all event handler functions.

Therefore, all links behave identically.

The simplest solution (without a complete overhaul):

for(var index in arrayOfRedirectLinks) {      
  (function(index) {
    var reference = arrayOfRedirectLinks[index];
    pageLinkElements[index].addEventListener('click', function(event) {
      event.preventDefault();
      window.location = reference;
    });
  }(index));
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using jQuery and JavaScript, start and stop a loop using a single button click

I am looking to create a button that will trigger a JavaScript function when clicked, causing a loop within it to start processing. If the button is clicked again before the loop completes, I want the loop to stop. And if clicked after the loop finishes on ...

Is the functionality of the jquery trigger('resize') limited to only one instance?

Here are two code snippets that I have: function adjustFooter() { var wrapper = $('#disqus_wrapper').height(); if ( wrapper > 200 ) { $(window).trigger('resize'); } }; var element = $('#disqus_wrapper&apos ...

The Mongoose findOne function encountered an error when attempting to convert a value to ObjectId

Trying to solve a perplexing exception that has left me scratching my head for over an hour. Error message: CastError: Cast to ObjectId failed for value "[email protected]" at path "_id" for model "Account" My goal is to fetch an Account using the ...

Access Azure-Active Directory through cypress tests

Currently, I'm faced with the task of creating automated tests for an application that requires login to Azure Active Directory. These tests are being written using Cypress and TypeScript. In search of a solution, I am seeking advice on how to execute ...

ToggleClass is not being applied to every single div

I am currently designing a pricing table with hover effects. You can view the progress here: Upon hovering on a pricing table, all the divs are toggling classes which is not the desired behavior. I want each element to have its own separate interaction. ...

Struggling to design a button that can delete tasks from the list, but encountering issues with the filter function

Although I am able to push values, I seem to be struggling with the filtering process. Can anyone provide guidance on whether I am using the filter method incorrectly or if there is another issue at play? import { useState } from 'react'; const ...

What are the best ways to identify memory leaks in a react native application?

In my react native Android learning management system app, I have utilized AsyncStorage for simpler state management instead of using redux. However, a major issue I am currently facing is that the app slows down significantly when used continuously to per ...

Tips for displaying the html content saved in the database onto an ejs webpage

This task seems simple, but I'm struggling to solve it. In my Node.js/Express webapp, I have the Quill.js editor installed. It stores my description in MySQL DB like this: <p><strong>This is the quill data. How are we doing dev?</stron ...

It is possible to create a perilous C function with a distinct prototype

Here's a puzzling question that has caught my attention recently. It turns out that in C, it is possible to create a function that deviates from its function prototype in the parameters being passed, as long as the function doesn't know the funct ...

Utilize the Link_to function within a Rails jQuery script

Trying to incorporate link_to (<%= link_to 'address' , boat_path("'+boat_id+'") %>) within my jQuery code has presented a challenge due to concatenation issues preventing it from functioning as intended. Below is the jQuery code ...

What is the best way to add a timeout to a $.ajax request and retry if it exceeds the set time limit?

Could someone provide me with a practical example of how to set a timeout for my $.ajax request and reattempt the request if it times out? I have gone through the documentation but still don't quite understand it. Any help would be greatly appreciated ...

Emulating navigator.onLine in Node.js

I have recently started working on a Node.js application. To further my understanding of Node, I am developing a utility app for network detection purposes. Within this app, my primary objective is to determine: a) whether my computer is connected to the ...

Guidelines for creating a dynamic filter in Prisma js

I am looking to create a dynamic filter based on user input from the frontend. On mapping the data, I found that the object results appear like this: { id: '2', name: 'yuhu' } The keys 'id' and 'name' need to be dyn ...

Styling the sub-elements using CSS in JavaScript

Currently, I am dealing with two CSS classes: .dragbox and .removebutton. The .dragbox represents a div, while the .removebutton is a button nested within the div. On my page, there are multiple dynamically generated instances of .dragbox. I am seeking ...

Tips for updating datatables following an ajax request

I've attempted various methods to refresh my data table following an AJAX Call but have had no success. I have tried using the draw() and ajax.reload() functions, but nothing seems to be working. Do you have any suggestions on how I can successfully r ...

Check if the browser is not Google Chrome before running the code in JavaScript

Related Search: Finding a secure way to detect Google Chrome using Javascript? Using Javascript to identify Google Chrome and apply CSS changes Is there a JavaScript statement that can be used to execute code only when the user is not using Google ...

Tips for clearing state when simply refreshing a DataTable on the page

When it comes to a datatable on a page, I am facing a unique challenge. I want the datatable to be refreshed with a clear state (no column order, etc.), but if the page is accessed by pressing the back button, it should retain its state. I have experiment ...

Looking for a solution to organize the dynamically generated list items in an HTML page

I am currently working on a movie listing website where all the movies are displayed in sequence based on their #TITLE#. The webpage is generated automatically by the software using a template file. Here is the section of code in the template file that sho ...

"Error: The functionality of finding places on Google Maps is not

I've encountered an issue while trying to integrate Google Maps into my Node application. The map is loading correctly and I'm able to retrieve my location. However, I am facing a problem with implementing the Google Places API code to allow user ...

Retain the chosen values even after submitting the form

Consider the following form: <form method="get" action=""> <select name="name"> <option value="a">a</option> <option value="b">b</option> </select> <select name="location"> <opt ...