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--tabwill 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__hiddenwill remove thehiddenattribute from the element. More generally, you can usejs-with-js--remove-attribute__<attribute-name> - Adding a class:
js-with-js--add-class__sr-onlywill add thesr-onlyclass to the element. More generally, you can usejs-with-js--add-class__<class-name> - Removing a class
js-with-js--remove-class__margin-top-0will remove themargin-top-0class. 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.jsonfile does have amodulefield pointing tosrc/index.jswhich 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
withJSlooks 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:
cleanto clean thedistdirectorylintfor linting the JS filestestfor running the testsbuildfor building the browser and CommonJS files. It'll trigger thepostbuildscript to minify the browser buildreleasetriggers a release to NPM. It'll automatically runprepackwhen creating the package to build the latest version of the library