Here's a version that caches by `.postInfo` id:

```coffee
RelativeDates =
  INTERVAL: $.MINUTE
  init: ->
    Main.callbacks.push @node

    # flush when page becomes visible again
    $.on d, 'visibilitychange', @flush

  # cache `dateEl.dataset.utc`
  cache: {}
  node: (post) ->
    dateEl = $ '.postInfo > .dateTime', post.el

    # Show original absolute time as tooltip so users can still know exact times
    # Since "Time Formatting" runs `node` before us, the title tooltip will
    # pick up the user-formatted time instead of 4chan time when enabled.
    dateEl.title = dateEl.textContent

    # convert and cache data-utc to milliseconds
    RelativeDates.cache[dateEl.parentNode.id] = utc = dateEl.dataset.utc * 1000

    diff = Date.now() - utc

    dateEl.textContent = RelativeDates.relative diff
    RelativeDates.setUpdate dateEl, diff

    # Main calls @node whenever a DOM node is added (update, inline post,
    # whatever), so use also this reflow opportunity to flush any other dates
    # flush is debounced, so this won't burn too much cpu
    RelativeDates.flush()

  # diff is milliseconds from now
  relative: (diff) ->
    unit = if (number = (diff / $.DAY)) > 1
      'day'
    else if (number = (diff / $.HOUR)) > 1
      'hour'
    else if (number = (diff / $.MINUTE)) > 1
      'minute'
    else
      number = diff / $.SECOND
      'second'

    rounded = Math.round number
    unit += 's' if rounded isnt 1 # pluralize

    "#{rounded} #{unit} ago"

  # changing all relative dates as soon as possible incurs many annoying
  # redraws and scroll stuttering. Thus, sacrifice accuracy for UX/CPU economy,
  # and perform redraws when the DOM is otherwise being manipulated (and scroll
  # stuttering won't be noticed), falling back to INTERVAL while the page
  # is visible.
  #
  # each individual dateTime element will be added to the stale list when it
  # needs to change.
  stale: []
  flush: $.debounce($.SECOND, ->
    # no point in changing the dates until the user sees them
    return if d.hidden
    for dateEl in RelativeDates.stale
      if d.contains dateEl # not removed from DOM
        diff = Date.now() - RelativeDates.cache[dateEl.parentNode.id]
        dateEl.textContent = RelativeDates.relative diff
        RelativeDates.setUpdate dateEl, diff
    RelativeDates.stale = []
    clearTimeout RelativeDates.timeout
    RelativeDates.timeout = setTimeout RelativeDates.flush, RelativeDates.INTERVAL)

  # Add element to stale list when the relative date string would change
  # diff is in milliseconds between dateEl and now
  setUpdate: (dateEl, diff) ->
    delay = if diff > $.HOUR
      diff % $.HOUR
    else if diff > $.MINUTE
      diff % $.MINUTE
    else
      diff % $.SECOND
    setTimeout (-> RelativeDates.stale.push dateEl), delay
```

I'm not really convinced it's worth it, however. It's trading two DOM methods + multiplication for two DOM methods, string hashing, and code clarity.