Стандартная функция render в timeago.js работает с
DOM-элементами через атрибут datetime, обновляя
textContent. Для нестандартных сценариев — другие атрибуты,
вывод в лог, работа в Node.js — создаются собственные рендеры.
title или
aria-label вместо textContent.datetime.Простейший самодельный рендер:
import { format } from 'timeago.js';
function createRenderer(element, dateStr, locale = 'ru') {
function update() {
element.textContent = format(dateStr, locale);
}
update(); // первое обновление
const timer = setInterval(update, 60000); // каждую минуту
return {
stop() {
clearInterval(timer);
}
};
}
const el = document.getElementById('time');
const renderer = createRenderer(el, '2025-05-26T08:00:00Z', 'ru');
// Остановить при уничтожении компонента
renderer.stop();
Умный рендер меняет частоту обновлений в зависимости от давности даты:
import { format } from 'timeago.js';
function adaptiveRenderer(element, dateStr, locale = 'ru') {
let timer = null;
function getInterval(diffMs) {
if (diffMs < 60000) return 10000; // < 1 мин: каждые 10с
if (diffMs < 3600000) return 60000; // < 1 ч: каждую минуту
if (diffMs < 86400000) return 3600000; // < 1 д: каждый час
return 86400000; // иначе: раз в день
}
function update() {
const diff = Math.abs(Date.now() - new Date(dateStr).getTime());
element.textContent = format(dateStr, locale);
clearTimeout(timer);
timer = setTimeout(update, getInterval(diff));
}
update();
return { stop: () => clearTimeout(timer) };
}
import { format } from 'timeago.js';
function renderTitle(elements, locale = 'ru') {
const timers = [];
elements.forEach(el => {
const date = el.getAttribute('datetime');
function update() {
el.setAttribute('title', format(date, locale));
}
update();
const id = setInterval(update, 60000);
timers.push(id);
});
return {
cancel() {
timers.forEach(clearInterval);
}
};
}
import { format } from 'timeago.js';
function renderAccessible(elements, locale = 'ru') {
elements.forEach(el => {
const date = el.getAttribute('datetime');
function update() {
const relative = format(date, locale);
el.setAttribute('aria-label', relative);
el.textContent = relative;
}
update();
setInterval(update, 60000);
});
}
import { format } from 'timeago.js';
function renderDataDate(locale = 'ru') {
const elements = document.querySelectorAll('[data-date]');
elements.forEach(el => {
const date = el.dataset.date;
function update() {
el.textContent = format(date, locale);
}
update();
setInterval(update, 30000);
});
}
Для серверного рендеринга или генерации шаблонов:
import { format } from 'timeago.js';
function renderToString(dates, locale = 'ru') {
return dates.map(date => ({
date,
label: format(date, locale),
}));
}
const items = renderToString([
'2025-05-26T08:00:00Z',
'2025-05-25T14:00:00Z',
], 'ru');
import { format } from 'timeago.js';
function animatedRenderer(element, dateStr, locale = 'ru') {
function update() {
const newText = format(dateStr, locale);
if (element.textContent !== newText) {
element.classList.add('time-updating');
element.textContent = newText;
setTimeout(() => {
element.classList.remove('time-updating');
}, 300);
}
}
update();
return setInterval(update, 60000);
}
CSS:
.time-updating {
opacity: 0.5;
transition: opacity 0.3s;
}
Обновлять время при изменении атрибута datetime:
import { format } from 'timeago.js';
function watchAndRender(element, locale = 'ru') {
function update() {
const date = element.getAttribute('datetime');
if (date) element.textContent = format(date, locale);
}
update();
const observer = new MutationObserver(mutations => {
if (mutations.some(m => m.attributeName === 'datetime')) {
update();
}
});
observer.observe(element, { attributes: true });
return {
stop() {
observer.disconnect();
}
};
}
Для плавного обновления без мерцания:
import { format } from 'timeago.js';
function rafRenderer(elements, locale = 'ru') {
let running = true;
let lastUpdate = 0;
function loop(timestamp) {
if (!running) return;
if (timestamp - lastUpdate > 10000) { // не чаще раза в 10 секунд
elements.forEach(el => {
el.textContent = format(el.getAttribute('datetime'), locale);
});
lastUpdate = timestamp;
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
return { stop: () => { running = false; } };
}