A declarative way to add or remove attributes and classes on HTML elements when JavaScript is loaded. Extensible to allow running other kind of updates.
Some attributes, classes and styles are only relevant once JavaScript has loaded:
- ARIA properties or roles setting expectations for assistive technologies that are only fullfiled by JavaScript
- Some fallback HTML that needs hiding once JavaScript is there, because an enhanced widget takes over the feature
- Or inversely some HTML that needs revealing to provide a better experience, but only when JavaScript is present.
Because there's no guarantee JavaScript will load, they should not be present in the HTML that's downloaded by browsers. This library provides a declarative way (through either data attributes or classes) to quickly amend the initial markup once JavaScript has loaded, ensuring a clean experience when it doesn't.
The library will look for elements marked by specific classes in your HTML. Out of the box, withJS allows you to run the following operations by adding classes to your HTML elements:
- Removing the element from the DOM:
js-with-js--remove
- Adding an attribute:
js-with-js--add-attribute__role--tab
will add therole="tab"
to the element. More generally, you can usejs-with-js--add-attribute__<attribute-name>--<attribute-value>
. - Removing an attribute:
js-with-js--remove-attribute__hidden
will remove thehidden
attribute from the element. More generally, you can usejs-with-js--remove-attribute__<attribute-name>
- Adding a class:
js-with-js--add-class__sr-only
will add thesr-only
class to the element. More generally, you can usejs-with-js--add-class__<class-name>
- Removing a class
js-with-js--remove-class__margin-top-0
will remove themargin-top-0
class. More generally, you can usejs-with-js--remove-class__<class-name>
You might have noticed the js-with-js--<operation-name>__<argument1>--<argument2>
in all these classes. You can extend the library to provide your own operations to suit your needs (see bellow).
-
Install the package with your favourite package manager
npm install @cookieshq/with-js
or
yarn add @cookieshq/with-js
-
Import the library and call
withJS()
in your project:-
using ES6 imports
import { withJS } from '@cookieshq/with-js/src/index' withJS();
Note: The
package.json
file does have amodule
field pointing tosrc/index.js
which should allow to just import@cookieshq/with-js
. However, specifying the whole path in the import was the most reliable way to get it working across the major bundlers (Webpack, Rollup and Parcel). -
using Common JS imports
const withJS = require('@cookieshq/with-js') withJS();
-
with a
<script>
tag in your HTML<script src="https://unpkg.com/@cookieshq/with-js/dist/with-js.iife.min.js" defer></script> <script async> document.addEventListener('DOMContentLoaded', function() { withJS(); }); </script>
-
You have to explicitly call
withJS()
for the updates to get applied. This lets you be in control of when and with which options it runs.
Without any arguments, withJS()
will hunt for all elements with a class that contains js-with-js--
in the document
. You can also:
- pick a specific element to update with
withJS(element)
- select other elements in the DOM with a CSS selector
withJS(selector)
- restrict where
withJS
looks for the elements to update withwithJS({parent: element})
- which can also be combined with using your own selector
withJS(selector, {parent:element})
, or less verboselywithJS(selector, parentElement)
Internally the library run()
s the updates()
it extracts from each target element(s). You can override both behaviors by providing your own functions in a final hash parameter:
withJs({run: customRunFunction, updates: customUpdatesFunction});
withJs({parent: element, run: customRunFunction, updates: customUpdatesFunction});
withJs(element,{run: customRunFunction, updates: customUpdatesFunction});
withJs(selector,{run: customRunFunction, updates: customUpdatesFunction});
withJs(selector, parent,{run: customRunFunction, updates: customUpdatesFunction});
You can add extra options by providing your own run
function
that passes custom list of operation to the default implementation.
import { withJS, applyUpdates,AVAILABLE_OPERATIONS } from '@cookieshq/with-js';
withJS({
run: function(operations, el) {
return applyUpdates(operations, el, {
availableOperations: {
...AVAILABLE_OPERATIONS,
// This would now allow things like `js-with-js--set-style__display--none`
'set-style': function(element,property, value) {
element.style[property] = value;
}
}
})
}
})
Maybe you find the js-with-js--
prefix a bit verbose,
or prefer different separators. You can provide a custom updates
function that provides different options to the default implementation.
import { withJS, getUpdatesFromClasses } from '@cookieshq/with-js';
withJS({
updates: function(el) {
// This would let you have class names like: `js--add-attribute:role,tabpanel`
// As we're not looking to use the class for styling, the "special" characters
// in the class won't be much of a bother.
return getUpdatesFromClasses({
marker: 'js--',
operationToArgumentsSeparator: ':',
argumentToArgumentSeparator: ','
})
},
target: '[class*="js--"]'
})
- The project is build with Rollup, with Babel for compiling to ES5.
- Tests are run with Ava
- Linting with ESLint is set up on the project and should be triggered on commit thanks to Husky and lint-staged
- The project uses np to manage NPM releases
The build commands are managed through npm scripts, mostly pass through to one of the modules above:
clean
to clean thedist
directorylint
for linting the JS filestest
for running the testsbuild
for building the browser and CommonJS files. It'll trigger thepostbuild
script to minify the browser buildrelease
triggers a release to NPM. It'll automatically runprepack
when creating the package to build the latest version of the library