@onenexus/squery

Interact with Synergy modules/BEM DOM elements

Usage no npm install needed!

<script type="module">
  import onenexusSquery from 'https://cdn.skypack.dev/@onenexus/squery';
</script>

README

GitHub license Inline docs Build Status npm version npm downloads codecov

Interact with Synergy modules/BEM DOM elements

Overview

sQuery is used for interacting with DOM elements that follow the Synergy naming convention.

Learn how to integrate with React components

Example
<div class="foo">
    <div class="foo_bar"><div>
    <div class="foo_bar-alpha"><div>
</div>

<div class="foo-fizz">
    <div class="foo_bar"><div>
    <div class="foo_bar-beta"><div>
</div>
sQuery('foo').getComponents('bar').addModifier('buzz');
Result
<div class="foo">
    <div class="foo_bar-buzz"><div>
    <div class="foo_bar-alpha-buzz"><div>
</div>

<div class="foo-fizz">
    <div class="foo_bar-buzz"><div>
    <div class="foo_bar-beta-buzz"><div>
</div>

Why?

In a world where DOM elements are becoming more structured and modular, querying DOM elements via classes quickly makes your code become WET, affecting things like readability, scalability and maintainability. The Synergy naming convention (as used above) keeps the DOM as clean and DRY as possible by only requiring one class per element, even if that element has modifiers (learn more).

Consider attempting something like the above using vanilla JS:

// get all `foo` modules
const foos = document.querySelectorAll('.foo, [class*="foo-"]');

foos.forEach(foo => {
    // get all `bar` components
    const bars = foo.querySelectorAll('.foo_bar, [class*="foo_bar-"]');

    bars.forEach(bar => {
        // replace old class with new class
        bar.classList.forEach(className => {
            if (className.indexOf('foo_bar') === 0) {
                bar.classList.replace(className, `${className}-buzz`);
            }
        });
    }
});

Some issues here include:

  • Repeating the module name foo
  • Repeating the component name bar
  • Awkward querySelectorAll query (required to allow the 'one class per element' paradigm)
  • Hard coded component/modifier glue (typically a non-issue, but still...)
  • Order of magnitude more code required than initial 1 line example

Something like this would be a common occurance in projects that use the Synergy naming convention (and indeed any convention that requires you to query DOM elements and manipulate their classList property, such as BEM), so by identifying and abstracting these commonly-occurring behaviours into their own API, sQuery allows you to interact with structured DOM elements effortlessly by targeting modules and components and adding/removing modifiers to them, allowing you to achieve the above with that one liner from earlier:

sQuery('foo').getComponents('bar').addModifier('buzz');

Checkout these other One-Nexus tools for working with Synergy modules:

  • Cell - Style DOM elements that follow the Synergy naming convention (including BEM) using Sass
  • Polymorph - Style DOM elements that follow the Synergy naming convention (including BEM) using JavaScript
  • Lucid - A set of Higher-Order React Components for rendering UI elements that follow the Synergy naming convention
  • Synergy - A front-end framework for creating modular, configurable and scalable UI components (all of the above in one packaged framework)

Installation & Setup

Want to render your DOM elements with React, style them with JavaScript, and interact with them using sQuery? Checkout the Synergy framework instead

npm install --save @onenexus/squery
import 'sQuery' from '@onenexus/squery';

sQuery.init(); 

See the sQuery.init() method for advanced setup

Using BEM? Checkout the Working With BEM page

API

Usage

The above APIs can be used in the following ways:

Return value of sQuery() function (Recommended)

  • Element(s) retreived via a Synergy query
  • Config automatically deteced from global Synergy object

See the sQuery() page for more information

sQuery(query)[method](...args);
Example
sQuery('.accordion').getComponents('panel');

Property of sQuery

  • Element(s) must be explicitly passed as the first argument
  • Config must be explicitly passed via the config argument
sQuery[method](node, ...args, config);
Example
sQuery.getComponents(document.querySelectorAll('.accordion'), 'panel', {
    componentGlue: '_',
    modifierGlue: '-'
});

Via Element/NodeList Prototype

This may be useful and convenient, but use with caution as it is recommended to avoid modifying JavaScript prototypes

Element[method](...args);
NodeList[method](...args);
Example
document.querySelectorAll('.accordion').getComponents('panel');