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

scoped-attributes

v0.1.0

Published

Collect attributes from your DOM elements with a structured naming scheme

Downloads

2

Readme

Scoped attributes

A tiny, low-level, helper library to extract one or multiple attributes from a DOM element, based on a structured naming scheme.

Allows your elements to declare attributes in the form of data-attribute, data-scope-attribute, data-parentscope-scope-attribute,... and helps you gather them from most specific to least specific.

It is built to support a core set of features (see right after) and exposes all its internals for you to extend.

Features

Let's take the following button, meant to trigger some action and show some feedback to users in response.

<button 
  data-action="do-something" 
  data-feedback='{"type": "toast"}'
  data-success-feedback='{"message": "Oh yeah!"}'
  data-error-feedback='{"type": "Whoops! Little hiccup."}'>

The scopedAttributes function is a foundation for building an accessor that'll collect the feedback settings to notify users of either a successful or failed operation and merge them as common defaults.

const element = document.querySelector('button');
// Say we want the success attributes
const attributes = scopedAttributes(element, ['success', 'feedback']);
// At that point we've only collected an array of strings
// ['{"message": "Oh yeah!"}', '{"type": "toast"}']

// We can then parse and merge them to get a final value
const settings = Object.assign( // Shallow merge on top of each other
  attributes
    .map(JSON.parse)
    .reverse() // So we merge more specific settings on top of least specific ones
);

What to do with the collected array is up to you. You can decide to only pick the most specific (first) value for example, or parse the strings with parseInt for example.

You'll likely want to encapsulate the parsing and processing in your own function, that you can reuse elements after elements for consistency. For example:

function scopedObjectAttributes(element, scopes) {
  const attributes = scopedAttributes(element, scopes);

  return Object.assign(
    attributes
      .map(JSON.parse)
      .reverse()
  );
}

function scopedIntAttributes(element, scopes) {
  const attributes = scopedAttributes(element, scopes);

  return parseInt(attributes[0]);
}

Notable features

  • The function filters out any empty value (or any falsy value, if you're using custom parsing)
  • The function can also accept a single attribute name

Installation

The library is published on NPM

npm install scoped-attributes
yarn add scoped-attributes

The package provides an ESM module which should get picked up by your bundler of choice when just importing scoped-attributes:

import { scoped-attributes } from 'scoped-attributes';
scopedAttributes(element, scopes);

An ES module can be loaded directly in the browser with:

<script type="module">
import {scoped-attributes} from "./node_modules/scoped-attributes/dist/scoped-attributes.esm.js"
scopedAttributes(element, scopes);
</script>

For older browsers, the package also provides and ES5 UMD build to support older browsers:

<script src="./node_modules/scoped-attributes/dist/scoped-attributes.es5.js" defer></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
  scopedAttributes(element, scopes);
});
</script>

Both have a minified counterpart with a .min.js extension, with an associated sourcemap.

Extensibility options

The scopedAttributes function allows you to provide options passing an Object as third optional argument. This will let you configure:

  • how the attribute name is built from a given list of scopes allowing you to set a prefix, or read other attributes than data-attributes
  • how attributes are read, allowing you to pre-process the attribute as it is read, for example if you already have functions for reading attributes as specific type of data

Little reminder that the scope of the function is only to gather the attribute values. Picking the right one/merging them is left to you (like in the Features example)

Building the attribute name

Passing a custom function as attributeName option lets you take over the computation of the attribute name. The function will receive the reduced list of scopes as list of arguments for the current attribute being looked up.

For example, going through ['action','success','feedback'], it'll get called 3 times with:

  1. ['action', 'success', 'feedback']
  2. ['success','feedback']
  3. ['feedback]

Up to you to turn those into an attribute name. You could decide to:

  • prefix the attribute name, to separate them from other libraries attributes:
const PREFIX = 'fwk';

function attributeName(...scopes) {
  `data-${PREFIX}-${scopes.join('-')}`
}

const attributes = scopedAttributes(element, scopes, {attributeName});
  • or not combine the scopes at all and use the first one only, which would let you access non-data attributes
function attributeName(scope) {
  return scope;
}

const attributes = scopedAttributes(element, scopes, {attributeName});

Customising the parsing

To customise the parsing, you can provide a custom getAttribute function. It'll receive the element and the attributeName currently being looked up, letting you parse it, sanitize it or whatnot.

Returning a falsy value will get the attribute filtered out from the result.

  • another way to get JSON attributes, for example:
function getAttribute(element, attributeName) {
  try {
    return JSON.parse(element.getAttribute(attributeName));
  } catch {
    // We just want to ignore unparseable values
  }
}

const attributes = scopedAttributes(element, scopes, {getAttribute});
  • or a way to parse dates
function getAttribute(element, attributeName) {
  try {
    return Date.parse(element.getAttribute(attributeName));
  } catch {
    // We just want to ignore unparseable values
  }
}

const attributes = scopedAttributes(element, scopes, {getAttribute});