npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@optimics/article-tracker

v0.7.2

Published

Report article content consumption data into Data Layer

Downloads

10

Readme

@optimics/article-tracker

Track article content consumption based on DOM events

The ArticleTracker connects to the article container DOM element and tracks its contents using specific ArticleElement classes and then provides unified interface ready for reporting.

Installation

npm install @optimics/article-tracker

Usage

The basic usage could look like this, if you need to only track article paragraphs.

import { ArticleTracker, ArticleParagraph } from '@optimics/article-tracker'

const articleElement = document.querySelector('#article')
const articleTracker = new ArticleTracker(articleElement, {
  contentTypes: [
    ArticleParagraph
  ]
})

articleElement.on('elementsDisplayed', () => {
  // Connect to your tracking scripts
  console.log(articleTracker.getMetrics())
})
articleElement.on('elementsConsumed', () => {
  // Connect to your tracking scripts
  console.log(articleTracker.getMetrics())
})
articleElement.track()

ArticleParagraph

The ArticleParagraph is a basic Element Type, that only tracks <p> tags content. It estimates slowest and fastest reader time based on the text content size.

The default selector is just 'p'.

import { ArticleParagraph } from '@optimics/article-tracker'

Custom Element Type

import { ArticleTracker, ArticleParagraph, ArticleElement } from '@optimics/article-tracker'

class ArticleVideo extends ArticleElement {
  static selector = '.my-video-player'
  type = 'videos'

  getMetadata() {
    /* In this example, we assume, that video length is stored inside the DOM
     * element like this. It is up to you, to write your bindings
     * <div class="my-video-player" data-player-length="90" /> */
    return this.el.dataset.player
  }

  estimateFastestTime() {
    /* In this example, we assume, that the element can be considered consumed
     * after 75 % of the video length has been played */
    return this.getMetadata().length * 0.75
  }

  estimateSlowestTime() {
    return this.getMetadata().length
  }
}

const articleElement = document.querySelector('#article')
const articleTracker = new ArticleTracker(articleElement, {
  contentTypes: [
    ArticleParagraph,
    ArticleVideo,
  ]
})

Metrics object

The ArticleTracker metrics are split by the Content Types, but also includes aggregated values. When you call articleTracker.getMetrics, you might get something like this:

{
  "achieved": 0.05,
  "consumed": false,
  "overtime": 0,
  "timeTotal": 25,
  "content": {
    "paragraph": {
      "achived": 0.05,
      "consumed": false,
      "consumableElements": 4,
      "consumedElements": 0,
      "detected": 5,
      "displayed": 1,
      "timeTotal": 25,
      "estimates": {
        "fastest": 36.4,
        "slowest": 64.5
      }
    }
  },
  "estimates": {
    "fastest": 36.4,
    "slowest": 64.5
  }
}

The values in the root of the metrics object is aggregated from the individual elements.

achieved

Percentage of how much of the context have been consumed. Rounded to two decimals.

Available on: ArticleMetrics, ContentTypeMetrics

consumed

Each Content Type has specific measurement logic, that determines if it is appropriate to mark it consumed. Let's take ArticleParagraph for example.

The ArticleParagraph estimates the fastest and the slowest consumption time based on the amount of words in the paragraph. We measure time the paragraph spends on screen and when it reached the fastest consumption time, we mark it consumed.

The entire Article is consumed, when all of its elements have been consumed.

Available on: ArticleMetrics, ContentTypeMetrics

consumableElements

How many elements of this type can be consumed? Empty elements of a type are not counted into the consumption metrics. For example empty paragraph would be ignored.

Available on: ContentTypeMetrics

consumedElements

How many elements of this type have been consumed?

Available on: ContentTypeMetrics

detected

How many elements of this type have been detected?

Available on: ContentTypeMetrics

estimates.fastest

Calculated estimate of the fastest time required to consume the entire content in seconds.

estimates.slowest

Calculated estimate of the slowest time required to consume the entire content in seconds.

overtime

Extra time user spent consuming this content. This is a natural number multiplier of the slowest consumer time from TimeEstimates. User, that reached twice the time of slowest consumer will have value 1. Three times the slowest consumer will be 2, and so on. Useful for filtering out unuseful analytics metrics.

Available on: ArticleMetrics

timeTotal

How much time did user spend on the element or article in seconds.

Available on: ArticleMetrics, ContentTypeMetrics

Events

Article Tracker automagically triggers events as result of user interaction. The EventHandler is a function, that returns void and always receives props objects with at least articleTracker instance. All of the events are debounced, so they do not trigger too often.

To subscribe to ArticleTracker event, use the following subscription pattern:

function handler() {
  doSomething()
}
articleTracker.consumptionAchievement.subscribe(handler, options)

Supported events are described below. The options are optional and may be ommited.

  • once - when true, the event handler will be triggered only once and then unbound
  • conditions - filter the callbacks

consumptionAchievement

Reports progress of consumption achievement. Currently triggered together with elementsConsumed event but the API might change in the future to provide details with higher resolution.

articleTracker.on('consumptionAchievement', ({ articleTracker, achieved }) => {
  console.log(achieved)
})

consumptionStateChanged

Fired when the Article Tracker changes state between "no elements being consumed" and "some elements being consumed". The callback gets passed boolean property consuming. When consuming is true, it means, that some elements in the article are being consumed by the user.

articleTracker.on('consumptionStarted', ({ consuming }) => {
  console.log(consuming)
})

consumptionStarted

Fired when the article tracker changes state from "no elements being consumed" to "some elements being consumed".

articleTracker.on('consumptionStarted', ({ articleTracker }) => {
  console.log(articleTracker.getMetrics())
})

consumptionStopped

Fired when the article tracker changes state from "some elements being consumed" to "no elements being consumed".

articleTracker.on('consumptionStopped', ({ articleTracker }) => {
  console.log(articleTracker.getMetrics())
})

elementsDisplayed

This is triggered whenever an element, that has not been displayed on page yet, has been diplayed in the page viewport. The event receives targets prop, containing reference to all article elements, that have been displayed since last call.

articleTracker.on('elementsDisplayed', ({ articleTracker, targets }) => {
  console.log(articleTracker.getMetrics())
  console.log(targets)
})

elementsConsumed

This is triggered whenever an element, that has not been consumed yet, has met conditions to be marked as consumed. This event receives targets prop, containing reference to all article elements, that have been consumed since last call.

articleTracker.on('elementsConsumed', ({ articleTracker, targets }) => {
  console.log(articleTracker.getMetrics())
  console.log(targets)
})

overtime

articleTracker.on('overtime', ({ articleTracker }) => {
  console.log(articleTracker.getMetrics())
})

Event Filtering

Testing

The test suite is a mixture of two environments. JSDOM is used for interface testing and Puppeteer is used for the integration testing to leverage a real Chrome environment. Please note, that Puppeteer tests are not always stable, take long time to run and may sometimes fail due to timeouts and other various instabilities. Rerunning tests on sporadic failures is recommended.

The test suite is included in the repository main test suite.

Environment variables

You can use these variables to develop and debug Puppeteer tests.

TEST_GRAPHIC

Setting TEST_GRAPHIC=true will disable test headless mode and will open actual Chrome window, so you can see the literal behaviour with your own eyes. This is useful when you want to understand what is happening inside the browser.

The browser window will partially take control of your operating system window focus, so you might not be able to fully use your computer during the test. Make sure you select specific test you want to run.

TEST_DEBUG

Setting TEST_DEBUG=true will keep the browser window open for 60 minutes after the test has ended. It implies TEST_GRAPHIC. It is useful, when you want to inspect the Browser Developer Tools

Known issues

The test suite starts subprocesses, that occasionally fail to exit on unexpected test exit. Namingly, this is webpack-dev-server and Chrome browser. This will consume resources of your operating system, eventually rendering it unusable. If you experience issues, like heavy swapping and low memory, terminate them manually.

Unsafe workaround

This will terminate all processes named chrome and node. Do not use it unless you are sure what you are doing.

killall chrome
killall node