1
votes

I'm using some JS to calculate the time since last post (ie. "Posted 2 hours ago"):

function ago(date) {
function render(n, unit) {
    return n + " " + unit + ((n == 1) ? "" : "s") + " ago";
}

var seconds = Math.floor((new Date() - date) / 1000);

var interval = Math.floor(seconds / (60 * 60 * 24 * 30 * 365));
if (Math.floor(seconds / (60 * 60 * 24 * 30 * 365)) >= 1) {
    return render(interval, "year");
}
interval = Math.floor(seconds / (60 * 60 * 24 * 30));
if (interval >= 1) {
    return render(interval, "month");
}
interval = Math.floor(seconds / (60 * 60 * 24));
if (interval >= 1) {
    return render(interval, "day");
}
interval = Math.floor(seconds / (60 * 60));
if (interval >= 1) {
    return render(interval, "hour");
}
interval = Math.floor(seconds / 60);
if (interval >= 1) {
    return render(interval, "minute");
}
interval = Math.floor(seconds);
return render(interval, "second");
}

And then include it on the index page, next to each post:

var date = 
Date.parse(document.getElementById("postedon").getAttribute("datetime"));
document.getElementById("postedago").innerHTML = ago(date);

Here is the HTML:

<time id="{{ post.postedon }}" datetime="{{ post.date | date_to_xmlschema }}">
<span id="{{ post.postedago }}"></span>
</time>

Note that I'm setting the element's ID and attributes in each post's frontmatter using Liquid & YAML (with Jekyll):

---
date: 2018-1-4 02:00:00 +0100
postedago: postedago-post1
postedon: postedon-post2
---

The issue is I have to set the JS code for each post manually like this:

var date = 
Date.parse(document.getElementById("postedon").getAttribute("datetime"));
document.getElementById("postedago").innerHTML = ago(date);

var date = 
Date.parse(document.getElementById("postedon2").getAttribute("datetime"));
document.getElementById("postedago2").innerHTML = ago(date);

var date = 
Date.parse(document.getElementById("postedon3").getAttribute("datetime"));
document.getElementById("postedago3").innerHTML = ago(date);

Otherwise it won't display the time for all posts, just one.

I tried setting the element id in JS to the front matter parameter but it won't work (essentially having Jekyll replace liquid tags inside the JS file):

var date = 
Date.parse(document.getElementById("{{ post.postedon }}").getAttribute("datetime"));
document.getElementById("{{ post.postedago }}").innerHTML = ago(date);

I tried adding the front-matter dashes at the top of the js file but it still doesn't work (as referenced on SO here: Reach Jekyll Variables With JavaScript and Pass it Throught DOM Manipulation)

---
---
//rest of your JavaScript

Maybe there's another way of doing this that I haven't thought of?

Thanks for taking your time to read this, I really appreciate any help!

PS: Reference timeago script implementation on each post's page (not on the index page containing all posts): https://hejnoah.com/posts/timeago.html

4
Give a look at momentjsDurga

4 Answers

2
votes

You should give your HTML elements a class attribute so that you can iterate over all of them in a loop using getElementsByClassName().

HTML:

<time class="postedon" id="{{ post.postedon }}" datetime="{{ post.date | date_to_xmlschema }}">
    <span class="postedago" id="{{ post.postedago }}"></span>
</time>

JS:

const timeElems = document.getElementsByClassName('postedon');

for (let i = 0, len = timeElems.length; i < len; i++) {
  let date = Date.parse(timeElems[i].getAttribute("datetime"));
  timeElems[i].firstElementChild.innerHTML = ago(date);
}

Now you don't have to write out JS for each post manually. You could similarly use getElementsByTagName('time') assuming this is the only place you use that tag.

1
votes

this is very simple you can use some JS(/javascript-time-ago) ready..

just use as example below..

timeAgo.format(new Date()) ---> will output // "just now"

if you will give the date format to it, will give how much time ago.

1
votes

Another example of how to calculate how long ago it was.

function ago(date) {
  var units = [
    { name : 'year', ms: 1000 * 60 * 60 * 24 * 30 * 365 },
    { name : 'month', ms: 1000 * 60 * 60 * 24 * 30 },
    { name : 'fortnight', ms : 1000 * 60 * 60 * 24 * 14 }, // yep.. I head that was a thing.
    { name : 'day', ms: 1000 * 60 * 60 * 24 },
    { name : 'hour', ms: 1000 * 60 * 60 },
    { name : 'second', ms: 1000 *60 },
  ];

  var ms = new Date() - date;
  var item = units.find( item => ms / item.ms > 1 );
  if (!item) item = units[ units.length - 1 ]; // if not found, use the last one.
  var value = Math.floor( ms / item.ms );

  return value + " " + item.name + ((value == 1) ? "" : "s") + " ago";
}
1
votes

First things first, your ago function can be simplified and reduced.

Then I'd use the attribute starts with selector with querySelectorAll to fetch the elements, and use .forEach() to set the textContent from the attribute.

function render(n, unit) {
  return n + " " + unit + ((n == 1) ? "" : "s") + " ago";
}
const labels = ["year", "month", "day", "hour", "minute", "second"];
const products = [1, 60, 60, 24, 30, 365]
  .map((n, i, a) => a.slice(0, a.length-i).reduce((p, n) => p * n, 1));

function ago(date) {
  var seconds = Math.floor((new Date() - Date.parse(date)) / 1000);

  return labels.reduce((res, lab, i) => {
    const interval = Math.floor(seconds / products[i]);
    return res ? res : interval >= 1 ? render(interval, lab) : "";
  }, "");
}


document.querySelectorAll("[id^=postedon]").forEach(el =>
  el.querySelector('#' + el.id.replace('postedon', 'postedago'))
  .textContent = ago(el.getAttribute("datetime"))
);
<time id="postedon0" datetime="2018-1-4 02:00:00 +0100">
  <span id="postedago0"></span>
</time>
<time id="postedon1" datetime="2018-1-7 02:00:00 +0100">
  <span id="postedago1"></span>
</time>
<time id="postedon2" datetime="2018-1-9 02:00:00 +0100">
  <span id="postedago2"></span>
</time>

<time id="postedon3" datetime="2018-1-13 02:00:00 +0100">
  <span id="postedago3"></span>
</time>

Also note that I'm using .textContent instead of .innerHTML since you're not actually setting any HTML content.

Note also that Date.parse doesn't work with that date format in Firefox.