VAAA
VAAA

Reputation: 15039

jquery - add multiple timers associated to HTML divs

I have the following DIV containing multiple cards elements:

enter image description here

Each of those cards have the following HTML structure:

<div class="user-w">
                        <div class="avatar with-status status-green">
                          <img alt="" src="img/avatar1.jpg">
                        </div>
                        <div class="user-info">
                          <div class="user-date">
                            12 min
                          </div>
                          <div class="user-name">
                            John Mayers
                          </div>
                          <div class="last-message">
                            What is going on, are we...
                          </div>
                        </div>
                      </div>

Those cards are loaded dynamically using ajax. What I need is to attach to each <div class="user-w"> a stopwatch so I can change for example background color when elapsed time is 4 min or make it hidden when elapsed time reaches 6 min.

I was thinking on using SetInterval multiple times for I think this is not possible.

Each DIV card element should be totally independant in terms of timing from the others.

Any clue on how to do it correctly?

Upvotes: 1

Views: 415

Answers (5)

HMR
HMR

Reputation: 39270

The accepted answer can give you a lot of trouble if the ajax part

however_you_create_your_element;

attach cardElement to the DOM

Is replacing or adding elements. Ajax and processCards share cardlist and your ajax may remove items from DOM but leave them in cardlist.

You failed to mention if you replace the card list in your ajax or append new cards but the following solution would work either way.

To adjust to updating every minute and showing minutes you can change the following 2 lines:

const repeat = 1000;//repeat every second
const message = timePassed=>`${Math.round(timePassed/1000)} seconds`

to:

const repeat = 60000;//repeat every minute
const message = timePassed=>`${Math.round(timePassed/60000)} min`

(function(){//assuming cardContainer is available
  const container = document.querySelector("#cardContainer");
  const repeat = 1000;//repeat every second
  const message = timePassed=>`${Math.round(timePassed/1000)} seconds`
  const updateCards = function(){
    Array.from(container.querySelectorAll(".user-w .user-date"))
    .map(
      (element)=>{
        var started = element.getAttribute("x-started");
        if(started===null){
          started = Date.now()-
            parseInt(element.innerText.trim().replace(/[^0-9]/g,""),10)*60000;
          element.setAttribute("x-started",started);
        }
        return [
          element,
          Date.now()-parseInt(started,10)
        ];
      }
    ).forEach(
      ([element,timePassed])=>
        element.innerText = message(timePassed)
    );
  }
  setInterval(updateCards,repeat);
}());
      <div id="cardContainer">
        <div class="user-w">
            <div class="avatar with-status status-green">
              <img alt="" src="img/avatar1.jpg">
            </div>
            <div class="user-info">
              <div class="user-date">
                12 min
              </div>
              <div class="user-name">
                John Mayers
              </div>
              <div class="last-message">
                What is going on, are we...
              </div>
            </div>
        </div>
      </div>

Upvotes: 0

you want to add a function with setTimeout() for ajax success: parameter.

Ex(with jquery):-

    $.ajax({
    // your ajax process
     success:function(){
      setTimeout(function(){
       $('.card-w').not('.anotherclassname').addClass('someclassname-'+i);
       $('someclassname-'+i).addClass('.anotherclassname').fadeOut();
      },6000);
     }
    })

Upvotes: 0

Chirag Ravindra
Chirag Ravindra

Reputation: 4830

One way to do this is, after your AJAX call completes and the DOM has been updated, you can use jQuery to select your cards and for each card you can:

  1. Get the time value and parse it to convert it to milliseconds - you can write a simple helper function for this or use something like momentjs based on how complex your requirement is
  2. Use setTimeout with the parsed value and do any style updates/hiding as needed

Sample Code:

$('.user-w').each(function(i, el){
    var $el = $(el);
    var val = $el.find('div.user-date').html();
    val = parseTime(val) // Assuming a function to parse time from string to milliseconds is there
    setTimeout(function(){
        // Do any updates here on $el (this user card)
    }, val);
    /*setTimeout(function(){
        // Do something else when val ms is close to completion
        // here on $el (this user card)
    }, 0.9 * val);*/
})

If you want multiple things to happen (change bg color and then, later, hide element, for example) you can set multiple setTimeouts to happen with different time values derived from val

Upvotes: 0

hunteke
hunteke

Reputation: 3716

In general, shy away from the "attach everywhere" syndrome. Think in lists, and simple processors. You will thank yourself down the road, as will your users for more efficient code, and your maintenance programmer.

Accordingly, one thought process might be to setup an array of the elements in question, and use a single setInterval. Something like:

...
var cardList = [];

function processCards ( ) {
    var i, cardEl, cardStartTime, now;

    now = Date.now();
    i = -1;
    while ( ++i < cardList.length ) {
        cardEl = cardList[ i ][ 0 ];
        cardStartTime = cardList[ i ][ 1 ];
        if ( cardStartTime + 6 min < now ) {
            // do 6 minute thing
        }
        else if ( cardStartTime + 4 min < now ) {
            // ...
        }
    }
}

$.get('/your/new/cards/call')
.done(function(...){
    ...
    var now = Date.now();
    for ( i in returnedCards ) {
        cardElement = however_you_create_your_element;
        attach cardElement to the DOM

        // save reference to element and time created for later processing
        cardList.push([cardElement, now]);
    }

});

setInterval(processCards, 2*60*1000); // or whatever granularity you want.

One advantage of this approach over multiple setTimeout calls for each card is the simplicity of having a single processing function, rather than N copies lying around. It's easier to reason about and manage, and reduces the likelihood of errors if an element disappears before it's associated setTimeout function executes.

Upvotes: 0

JasonB
JasonB

Reputation: 6368

When you build the card from the ajax object, set a data element to store the timestamp on the card. Use setInterval to trigger a function that loops through all of the cards and checks their timestamps against the current time and updates the date on the ui, changes the bgcolor, or removes the element altogether.

Upvotes: 1

Related Questions