0

I'm trying to create a script with jQuery where it finds elements with the class "timeAgo" and replace its innerHTML with how long ago the time in the create attribute was. Here's what I have:

function timeAgo(time) {
  var d = new Date();
  var diff = Math.floor((d.getTime() - time)/1000);
  if (diff >= 31536000) {
    if (Math.floor(diff/31536000) == 1) {
      return (Math.floor(diff/31536000) + " year ago");
    } else {
      return (Math.floor(diff/31536000) + " years ago");
    }
  } else if (diff >= 2592000) {
    if (Math.floor(diff/2592000) == 1) {
      return (Math.floor(diff/2592000) + " month ago");
    } else {
      return (Math.floor(diff/2592000) + " months ago");
    }
  } else if (diff >= 86400) {
    if (Math.floor(diff/86400) == 1) {
      return (Math.floor(diff/86400) + " day ago");
    } else {
      return (Math.floor(diff/86400) + " days ago");
    }
  } else if (diff >= 3600) {
    if (Math.floor(diff/3600) == 1) {
      return (Math.floor(diff/3600) + " hour ago");
    } else {
      return (Math.floor(diff/3600) + " hours ago");
    }
  } else if (diff >= 60) {
    if (Math.floor(diff/60) == 1) {
      return (Math.floor(diff/60) + " minute ago");
    } else {
      return (Math.floor(diff/60) + " minutes ago");
    }
  } else {
    if (diff == 1) {
      return (diff + " second ago");
    } else {
      return (diff + " seconds ago");
    }
  }
}
$(document).ready(function () {
  $(".timeAgo").innerHTML(timeAgo($(".timeAgo").attr("create")));
}

But whenever I run the code with multiple instances of ".timeAgo", it sets all of them to the first instance of ".timeAgo". How do I make it so that it uses each separate instance?

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
wutadam
  • 73
  • 7
  • first: jQuery uses `.html()` and not `.innerHTML` (that is not a function, so no `()`); Second: you'll need to loop through the elements you've selected, maybe using `.each()` – Calvin Nunes Feb 18 '20 at 13:38

1 Answers1

2

Use each to loop through them, and $(this) within each to access each individual one:

$(".timeAgo").each(function() {
    var $el = $(el);
    $el.html(timeAgo($el.attr("create")));
});

Also note using html(), not innerHTML =. Although within the callback, there's really no need for jQuery:

$(".timeAgo").each(function() {
    this.innerHTML = timeAgo(this.getAttribute("create"));
});

In fact, you don't need jQuery at all in any modern browser (but this won't work in IE11 unless you follow my steps here to polyfill forEach):

document.querySelector(".timeAgo").forEach(function(element) {
    element.innerHTML = timeAgo(element.getAttribute("create"));
});

Side note: You appear to be creating elements with a non-standard create attribute. The WHAT-WG and W3C recommendation is not to do that, since it's always possible that attribute will be assigned standard meaning sometime in the future. Instead, use data-create (a data-* attribute) and access it via .attr("data-create"). (See my answer here for why you may or may not want to use .data("create") instead to access it.)

$(".timeAgo").each(function() {
    var $el = $(this);
    $el.html(timeAgo($el.attr("data-create")));
});

or

$(".timeAgo").each(function() {
    this.innerHTML = timeAgo(this.getAttribute("data-create"));
});

or (with polyfill for IE11 if needed)

document.querySelector(".timeAgo").forEach(function(element) {
    element.innerHTML = timeAgo(element.getAttribute("data-create"));
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Would be nicer using `bind()` or an arrow function instead of creating a self ref var... – marekful Feb 18 '20 at 13:43
  • 2
    @marekful - No, neither `bind` nor an arrow function is relevant above. The point of `each` is that jQuery sets what `this` is to each DOM element in the callback. The `$this` above **isn't** referring to `this`, but to the jQuery object created by calling the function `$(this)`. – T.J. Crowder Feb 18 '20 at 13:44
  • 1
    @marekful `$this` is just to not repeat `$(this)` – Roko C. Buljan Feb 18 '20 at 13:45