Altering icons using timeouts in a repeating sequence

So, I have this code that is supposed to loop through each record in a table and change the save icon to a checkmark for 2 seconds before changing it back. However, it's not working as expected.

I've used a similar method for other buttons which has worked fine. I suspect it might have something to do with the speed at which the loop processes each record, even though setTimeout should be asynchronous...

Is there a more efficient way to achieve this? I want each button to behave independently without resorting to writing a function that changes all icons at once.

const iconToggle = () => {
    const isCheckIcon = btn.firstElementChild.classList.contains('fa-check');
    if (isCheckIcon) {
        btn.innerHTML = '<i class="fa fa-save fa-2x"></i>';
    } else {
        btn.innerHTML = '<i class="fa fa-check fa-2x"></i>';
    }
}

for (row = 0; row < table.rows.length; row++) {
    currentRow = table.rows.item(row);

    ...

    returncode = save_row();

    btn = currentRow.getElementsByClassName('record-save')[0].firstElementChild;
    if (returncode == 0) {
        iconToggle();
        setTimeout(iconToggle, 2000);
    }
}

EDIT:

$('.table-save-all').on('click', 'i', function() {
    var table = document.getElementById('edit_history_table_body');

    const iconToggle = (abtn, state) => {
        if (state == "save") {
            abtn.innerHTML = '<i class="fa fa-save fa-2x"></i>';
        } else if (state == "check") {
            abtn.innerHTML = '<i class="fa fa-check fa-2x"></i>';
        }
    }

    var currentRow, key, TotalNoBreakDec, OvertimeDec, TotalDec, StartDec, HourSchedule, returncode, btn;

    // loop through each row of the table.
    for (row = 0; row < table.rows.length; row++) {
        currentRow = table.rows.item(row);

        ...

        returncode = save_row();

        btn = currentRow.getElementsByClassName('record-save')[0].firstElementChild;
        if (returncode == 0) {
            iconToggle(btn, "check");
            setTimeout(() => { iconToggle(btn, "save") }, 2000);
        }
    }

    btn = document.getElementsByClassName('table-save-all')[0].firstElementChild;
    iconToggle(btn, "check");
    setTimeout(() => { iconToggle(btn, "save") }, 2000);
});

Answer №1

For optimal functionality, it is recommended to pass the specific btn that needs to be modified as a parameter in the iconToggle() method. Currently, your loop iterates through all buttons and reassigns the -- potentially global -- btn variable. As a result, when the timeout triggers for resetting the icon, btn may be assigned to the last button in your list, causing multiple calls of iconToggle within the timeout to only affect this particular button.

Additionally, I suggest passing a predefined state into the iconToggle function, which will clearly indicate the intended change and prevent any unintentional modifications to your interface due to erroneous calls of iconToggle.

const iconToggle = (abtn, state) => {
    if (state == "save") {
        abtn.innerHTML = '<i class="fa fa-save fa-2x"></i>';
    } else if (state == "check") {
        abtn.innerHTML = '<i class="fa fa-check fa-2x"></i>';
    }
}
for (row = 0; row < table.rows.length; row++) {
    currentRow = table.rows.item(row);

    ...

    returncode = save_row();

    const btn = currentRow.getElementsByClassName('record-save')[0].firstElementChild;
    if (returncode == 0) {
        iconToggle(btn, "save");
        setTimeout(() => {iconToggle(btn, "check")}, 2000);
    }
}

UPDATE In response to your comment and revised code:

Please compare your code with mine. You will notice that I have

const btn = ... 

inside the loop's body, while you seem to have

btn = ... 

As a result, the line of code

setTimeout(() => {iconToggle(btn, "check")}, 2000);

in your version references the btn variable declared outside the loop's body (where exactly is not specified). Since you are constantly updating this btn variable throughout the loop and beyond, eventually btn points to this element

 btn = document.getElementsByClassName('table-save-all')[0].firstElementChild;

and this is the element that all callbacks of the timeouts apply the iconToggles to.

To resolve this issue, declare the btn variable inside the loop's body -- or preferably within the if statement -- to ensure proper functionality.

if (returncode == 0) {
    const btn = currentRow.getElementsByClassName('record-save')[0].firstElementChild;
    iconToggle(btn, "save");
    setTimeout(() => {iconToggle(btn, "check")}, 2000);
}

It is good practice to define variables within the narrowest scope possible to avoid such complications.

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

Loading asynchronous select options with a knockout observable array

I have an ajax-based asynchronous loader for select options. It accepts a remote address and returns an observable array that is correctly populated with descriptions and key values to be used in the following binding. <select data-bind="value: select ...

java code unicode feature in csharp

Here's the code I am using: $(document).ready(function () { var breadCrumps = $('.breadcrumb'); breadCrumps.find('span').text("<%= ArticleSectionData.title %>"); }); The 'title' property contains values en ...

Deactivate wheel event in jQuery

I am facing a challenge in designing my website using skrollr.js and fullpage.js libraries. Skrollr relies on the value of scrollTop to transform selected elements, while fullpage.js changes the top value of the viewport when scrolling, causing issues with ...

How can I address the issue of the Ionic ng-class function being activated by every ng-click event within the application

In my ionic list, I am using a function called getClass() to retrieve the class name for each iteration. However, this getClass() function is triggered not only when the list is populated, but also whenever there is an ng-click event in the app. For exampl ...

Identifying mouseover event across numerous elements in JavaScript

I'm attempting to identify the mouseover event on multiple elements using their class as a reference. For instance: <div class="myElement">1</div> <div class="myElement">2</div> <div class="myElement">3</div> & ...

Error in MongoDB Connection: Timeout issue caused by an unresolved Promise

Issue Overview Whenever I attempt to execute nodemon server, a timeout error is displayed indicating [nodemon] app crashed - waiting for file changes before starting.... Detailed Problem Description I have three files located at the following paths: ...

What is the best way to determine the reason for a npm dependency being installed?

Looks like this question may have been asked before, but I haven't been able to locate the answer in my search. I recall that NPM offers a command such as npm why module-name Or npm explain module-name This command helps you understand why a par ...

Toggle the checkbox to update the count

I am trying to implement a feature where the user can check a maximum of 4 checkboxes. I have managed to achieve this functionality in the code below. When the user unchecks a box, the count should decrease by one and they should be able to check another c ...

Tips for making an appended element match the height of an image

After embedding the div .soonOverlay into specific .smallCatalogBlock's, I am facing difficulty in adjusting the height of soonOverlay to match only the height of the img within smallCatalogBlock. Currently, its height extends throughout the entire co ...

Setting up Webpack for ES6 modules

Just dipping my toes into ES6! After completing all the webpack installation steps, I excitedly started creating some files following this picture https://i.sstatic.net/LYXCy.png Once I moved on to writing code in index.js and tried running it with the ...

Javascript doesn't seem to be executing properly after being echoed from PHP

I'm facing an issue where I am attempting to display some PHP code after a button click event in JavaScript, but the code is not executing and no errors are visible. <script type="text/javascript src="https://code.jquery.com/jquery- 3.4.1.min.js ...

How can Vuetify components be imported separately in the right way?

I’m currently getting acquainted with Vuetify and I’m finding it quite challenging to figure out how to import a specific component for use. My current version of Vuetify is 2.4.2 According to the documentation, they mentioned about the path to vueti ...

Choose a random element from a string with Javascript

Could someone please help me figure out why my code isn't functioning as expected? I'm trying to randomly select three names from a list and ensure that no name is repeated. While I believe I am on the right track, something seems to be missing. ...

Interacting Between PHP and Javascript

<form action="../"> <select onchange="window.open(this.options[this.selectedIndex].value,'_top')"> <option value="">Choose a zipcode </option> <option value="92507">92507</option> <option value=" ...

Utilizing identical variables across multiple Ajax functions

I have two AJAX functions within the same script and I need the output of the first function to be collected by the second one and sent to the correct URL. Here is my code: <!DOCTYPE html> <meta charset="utf-8"/> <html> <script s ...

Determining Equality in JavaScript: Comparing 2 Objects

I'm currently working with 2 objects that will always have the same amount of properties, but their values may vary. In my attempt to check if the two objects hold the same values, I decided to use the .every method. However, all results are returnin ...

Emulate the CSS hover effect with jQuery 코드 희미한

Is there a method in jquery or any other technology that can detect event X and trigger a different event elsewhere? Currently, I am working with an image that has an image map. When a user hovers over a specific area on the map, I would like another part ...

"Is there a JavaScript code snippet that can retrieve information from a JSON file

Is there a way to create a custom JavaScript function that can extract specific data from a JSON object based on certain keywords? I am working with a Task Manager API and would like to automatically populate fields based on the task title. Here is an exam ...

An Observable object generates output even if there is no listener to receive it

In my current Angular2 project, I am utilizing observables. It's my understanding that each Observable instance includes an observer on a one-to-one basis. By using observer.next(value) to emit something, we can then retrieve that value using observab ...

Differences between searching for elements using `find` and `querySelector

When working with AngularJS, I encounter an error when trying to use querySelector. angular.element(angular.element(e).querySelector('.open')).removeClass('open'); However, if I switch to using: angular.element(angular.element(e).fin ...