• Stars
    star
    530
  • Rank 83,660 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 11 years ago
  • Updated almost 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Lightweight JavaScript powered element queries

eq.js Build Status Coverage Status Code Climate Bower version

Element queries, fast and light

Element queries are the "holy grail" of responsive web design, allowing you to create a single component that can be dropped into any position in any layout and have them respond appropriately. Unfortunately, due to some hard-to-deal-with chicken-and-egg cases, especially involving inline elements, it's unlikely that element queries will make it into browsers any time soon.

eq.js aims to be a relatively easy to use drop-in solution to JavaScript powered element queries. Weighing in at about 2.6KB minified, around 1.1KB gzipped, and requiring no external dependencies, eq.js sets itself apart through size, speed, and ease of use. Simply drop eq.js on to your site and set the eq-pts attribute of your element (or set your points in Sass) and you're ready to go!

Installation

Installation is super easy. You can either pull down a copy from GitHub here, or you can install from Bower:

bower install eq.js --save

Then, add either eq.js or eq.min.js to your HTML, and you're ready to rock!

Usage

In order to use eq.js, you need to include eq.js on your site. Setting up element queries can be done in one of two ways: the first is to set up a data-eq-pts attribute on your desired element and the second is to use the eq-pts mixin in Sass. The first way is preferred, as it is faster for JavaScript to parse and can fire on DOMContentLoaded whereas the second way is slower and can only be fired on window load, increasing the likelihood of a flash of unstyled content.

Both methods have you write key: value pairs, with the key being the human-readable name of the applied state and the value being the min-width pixel width of the element you would like to set the state at.

With the first method, the value of data-eq-pts should be each pair and should be separated by a comma ,.

<div class="component" data-eq-pts="small: 400, medium: 600, large: 900">
  <h1>Hello World</h1>
</div>

You can add this attribute via JavaScript if you would like in the following way:

var component = document.querySelector('.component');
eqjs.definePts(component, {
  small: 400,
  medium: 600,
  large: 900
});

If you use the JavaScript method, you can only pass in a single DOM element at a time. It will return the string for data-eq-pts and add the data-eq-pts attribute to the element.

Similarly, with the second method, the eq-pts mixin is called with a map of your pairs. It is important not to quote your keys in the Sass map, or wonky things may happen in the parsing. At the bottom of your stylesheet, after all of your eq-pts have been called, you also need to call the eq-selectors mixin in order to write out the hook for eq.js.

.component {
  @include eq-pts((
    small: 400,
    medium: 500,
    large: 700
  ));
}

// ... at the end of the stylesheet

@include eq-selectors;

When eq.js has determined which state your element is in, it will add an data-eq-state attribute to the element set to the human-readable name of the min-width specified (along with any other states that have applied thus far). If the element is smaller than the smallest state, there will be no data-eq-state attribute. If you did not write your states in order, fear not, they will be sorted for you.

eq.js also adds window.eqjs to allow you to utilize eq.js in your own function calls. It will handle your DOMContentLoaded and load events as well as all resize events, inspecting your DOM to determine what nodes need to be queried each time.

If you dynamically add nodes that you would like to query, you need to trigger eq.js yourself. This is easy though! Just load up your nodes into an array or a NodeList and pass that to eqjs.query(nodes[, cb]), and eq.js will work its magic. eqjs.query() also takes a callback as an optional second argument that will be fired once all of the nodes have been processed. It will be passed an array of nodes that were worked on. You can also call eqjs.all([cb]) to run eq.js against all nodes in the DOM (with an optional cb callback).

Each node that gets queried will also fire an eqResize event once eq.js has worked its magic. This'll allow you to code reactively to what happens! The current value of data-eq-state will be available in event.details;

var myElement = document.getElementById('foo');

myElement.addEventListener('eqResize', function (e) {
  console.log('The current Element Query State is `' + e.details + '`');
});

Alternatively, you can use eqjs.refreshNodes() to update the listing of nodes that is use by eqjs.query() with all of the nodes currently in the DOM. This is useful when you know that a node has been dynamically added, but you don't have it as an object and can't pass it to ejs.query().

From there, proceed with styling as normal! Because eq.js uses attributes, you're going to want to select using attribute selectors. Styling follows the same patters as normal min-width media query styling, with styling for the base first, then subsequent styling added on top:

Sass

.container {
	border: 2px solid red;
	background-color: rgba(red, .25);
	
	&[data-eq-state$="small"],
	&[data-eq-state$="medium"],
	&[data-eq-state$="large"] {
	  font-size: 1em;
	}
	
	&[data-eq-state$="small"] {
	  border-color: green;
	  background-color: rgba(green .25);
	}
	
	&[data-eq-state$="medium"] {
	  border-color: orange;
	  background-color: rgba(orange, .25);
	}
	
	&[data-eq-state$="large"] {
	  border-color: blue;
	  background-color: rgba(blue, .25);
	}
}

CSS

.container {
  border: 2px solid red;
  background-color: rgba(255, 0, 0, 0.25);
}
.container[data-eq-state$="small"],
.container[data-eq-state$="medium"],
.container[data-eq-state$="large"] {
  font-size: 1em;
}
.container[data-eq-state$="small"] {
  border-color: green;
  background-color: rgba(0, 128, 0, 0.25);
}
.container[data-eq-state$="medium"] {
  border-color: orange;
  background-color: rgba(255, 165, 0, 0.25);
}
.container[data-eq-state$="large"] {
  border-color: blue;
  background-color: rgba(0, 0, 255, 0.25);
}

Bonus!

If you're using Sass, eq.js comes with a Sass partial, _eq.scss, that provides an eq mixin and an eq-contains mixin for handling element queries. Import it and use it like you would use a media query mixin, like the one provided by Breakpoint. The mixin will work with Sass 3.4 or greater or Libsass 3.2.0-beta.3 or greater. The above Sass example then becomes something like the following:

@import "eq";

.container {
	border: 2px solid red;
	background-color: rgba(red, .25);
	
	@include eq('small', 'medium', 'large') {
	  font-size: 1em;
	}
	
	@include eq('small') {
	  border-color: green;
	  background-color: rgba(green .25);
	}
	
	@include eq('medium') {
	  border-color: orange;
	  background-color: rgba(orange, .25);
	}
	
	@include eq('large') {
	  border-color: blue;
	  background-color: rgba(blue, .25);
	}
}

The eq-contains mixin will allow you to apply styling as long as that state is available in the data-eq-state list. Passing in a comma separated list is similar to an or media query in that at least one of those states must be active, passing in a space separated list is similar to an and media query in that all of the states must be active. Using eq-contains will allow styles to be built on top of each other.

If you're compiling with Compass, you're probably going to want to add your bower components directory to your import path to make importing _eq.scss easy. To do so, add something like the following to your config.rb file:

add_import_path "bower_components/eq.js/sass"

Browser Support

eq.js uses modern JavaScript, but can supports older browsers as well. It has been tested in the following browsers but is likely to support more:

  • IE8+ (see below for notes)
  • Firefox 3.5+
  • Chrome
  • Safari
  • Opera 10.0+
  • iOS Safari
  • Opera Mini
  • Android Browser
  • Blackberry Browser
  • Opera Mobile
  • Chrome for Android
  • Firefox for Android
  • IE Mobile

A note on IE8/Older Browser Support

There are two files provided: eq.min.js, eq.polyfilled.min.js, and polyfills.min.js. eq.polyfilled.min.js includes the polyfills needed to run eq.js in older browsers that are missing some newer JavaScript niceties and polyfills.js just includes the polyfills. The polyfills that come bundled will work for browsers IE9+. While these allow for a drop-in solutions using just what's provided here, a better solution (and if you need IE8 support, and where a bunch of the polyfills come from), is to consider using something like a polyfill service for a more robust and well-rounded solution.

The specific polyfills included are as follows:

Technical Mumbo Jumbo

eq.js has been tested in all modern browsers with thousands of nodes all requesting element queries. The limiting factor performance wise is JavaScript's native offsetWidth calculation, which is required for each element; hey, it's an element query after all! We work on reducing read/write layout thrashing by grouping reads separately from writes.

The process for determining which state to apply is primarily greedy for no state, then greedy for the largest state. If the element is neither smaller than its smallest state nor larger than its largest state, it then traverses each state to determine which state is correct. It does this by comparing one state to the next state up, ensuring that the current state is both greater than or equal to the defined min-width value and less than the next state's min-width.

Performance wise, the script handles itself very well even with thousands of nodes. With this current test setup of around 2.2k nodes, it can parse all of the nodes, calculate the size, and apply the proper attributes in about 35ms. We're employing requestAnimationFrame to reduce layout thrashing and produce smooth layout and resize. eq.js also comes with the full requestAnimationFrame Polyfill by Erik Möller and Paul Irish.

Be careful what changes you choose to make with this new found power. While element queries are great in theory, they can cause lots of heartache, especially when combined with inline elements. This script very consciously does not and will not attempt to recalculate element queries on all DOM changes as that is very likely to result in a never-ending rabbit hole of craziness. This, IMO, is one of the biggest things holding back element queries being implemented natively.

tl;dr

offsetWidth is slow, requestAnimationFrame reduces layout thrashing, eq.js is greedy for natural then largest states, with great power comes great responsibility.

More Repositories

1

respond-to

Compass extension to use Variable Driven respond-to mixin (https://gist.github.com/2493551) in your projects now!
Ruby
71
star
2

generator-armadillo

Static site generator for Yeoman
JavaScript
42
star
3

Aura

A Sass+Compass based Responsive Website framework
Ruby
39
star
4

vite-plugin-eleventy

Vite plugin to build out your site with Eleventy
JavaScript
34
star
5

Aurora

Drupal HTML5 Base Theme
CSS
33
star
6

training-glossary

Glossary of modern web development terms
29
star
7

borealis

Element query based responsive image solution
JavaScript
25
star
8

gulp-css-target

Gulp plugin for splitting a single CSS file into multiple CSS files
JavaScript
23
star
9

gulp-subtree

A Gulp module to push a given folder to a subtree
JavaScript
18
star
10

gulp-armadillo

The rebirth of the Armadillo static site generator, as a series of Gulp tasks!
JavaScript
16
star
11

stage-fright

Yet another web-based presentation library
JavaScript
16
star
12

houdini

An interactive introduction to CSS Houdini
HTML
15
star
13

generator-aurora

Yeoman generator for Aurora Drupal theme
JavaScript
12
star
14

generator-sketch

Small Yeoman generator to quickly spin up a small repo for testing
JavaScript
9
star
15

generator-snugug

Yeoman generator for stuff I find useful.
JavaScript
8
star
16

magic-tricks-with-houdini

Houdini Talk
HTML
8
star
17

rwd-sass-compass-training

Responsive Web Design with Sass and Compass training workshop files
Ruby
8
star
18

reparo

Repository maintainer
JavaScript
8
star
19

nightmare-ava

Nightmare with AVA
JavaScript
7
star
20

emoji-commit-types

Conventional Commit Types, but Emoji
JavaScript
7
star
21

Data-State

Small JavaScript helper for working with the `data-state` attribute of an element
JavaScript
7
star
22

static

Static site generation tool built with Grunt
JavaScript
5
star
23

designing-the-modern-web

JavaScript
4
star
24

blog

My personal blog
JavaScript
4
star
25

code-rwd-sass-compass

Code challenges for Advanced Responsive Web Design training
Ruby
4
star
26

be-kind

GitHub Comment Analyzer using Watson APIs
JavaScript
4
star
27

cbrew

Implementation of Cynthia Brewer, Mark Harrower, and The Pennsylvania State University's ColorBrewer color schemes as found here: http://www.personal.psu.edu/cab38/ColorBrewer/ColorBrewer_RGB.html in Sass
4
star
28

Outdated-UX-Patterns

5 Outdated UX Patterns Talk
CSS
3
star
29

config

Computer config files!
Shell
3
star
30

generator-vertex

Yet another Angular generator.
JavaScript
3
star
31

Compass-Options

A small Node module for parsing Compass's config.rb file into options for use in Node projects (especially Grunt/Gulp/etc…)
JavaScript
3
star
32

Style-Prototyping

JavaScript
3
star
33

RWD-with-Sass-Compass

My talk on Responsive Web Design with Sass+Compass
JavaScript
3
star
34

responsive-grids

Talk on Responsive Grids
CSS
2
star
35

code-get-sassy

Code for Sass Zero to Hero Training
Ruby
2
star
36

viewport-test

A test of different viewport tag settings
2
star
37

liten

A super tiny DOM manipulation library that is basically just syntactic sugar for ES5
JavaScript
2
star
38

yo-yo

Yo! CSS
CSS
1
star
39

foundry-helpers

A collection of Foundry VTT helpers
JavaScript
1
star
40

computer

Computer customizations
Shell
1
star
41

Compass-Drupal

True Drupal integration for Compass
1
star
42

Xbox-Spinner

A little test to see if I can recreate the Xbox Smart Glass spinner animation with CSS Animations
JavaScript
1
star
43

Sass-Compass-Demo-Page

Ruby
1
star
44

winning-the-web

The Web, let's win it.
JavaScript
1
star
45

lighthouse

LH Hackathon
JavaScript
1
star
46

OmNom-Drupal-Distro

Drupal 7 based Restaurant distrobution
1
star
47

sassy-fractions

Fractions for Sass! Spun out of Sassy Math
Ruby
1
star
48

snugug.github.io

Personal GitHub Page
CSS
1
star
49

generator-presentation

Yeoman Generator for my Presentation
JavaScript
1
star
50

get-sassy

Sass Zero to Hero Trainig
JavaScript
1
star
51

gulp-simple-compass

JavaScript
1
star
52

Drupal-Reset

Drupal Reset for Sass
1
star
53

svelte-mega-forms

Dynamic forms for Svelte
Svelte
1
star
54

dib-boot

Grunt DIB boostrap
CSS
1
star
55

Sassy-Lists

Advanced List handling for Sass
1
star
56

modern-web-redux

Designing the Modern Web training, as a talk
CSS
1
star
57

starchart-studio

Starchart Studio
Astro
1
star
58

Responsive-Ads

A Proof of Concept for an iFrame based Responsive Ad solution
Ruby
1
star
59

cf-deploy

Cloud Foundry Build and Deploy, specifically for Continuous Deployment
JavaScript
1
star
60

MTA-PDF-Applied

MTA real time train tracking; hackathon for PDF Applied
JavaScript
1
star
61

generator-drupal-theme

Yeoman generator for creating Drupal themes
1
star
62

mr-roboto

どうもありがとうミスターロボート
CSS
1
star
63

content-strategy-deliverables

Talk on Content Strategy Deliverables
CSS
1
star
64

Downstate-Young-Dems

New York State's Downstate Young Dems Drupal Stack
PHP
1
star
65

snugug-flowtime

CSS
1
star
66

people-first

It’s a strange and exciting time to be building things for the web. Our industry is changing quickly and the days of siloed roles, waterfall processes, and feature-first development are going, going, gone. From the smallest startup to the largest enterprise, the worlds of design and development are merging and are being re-centered around the user. The use of “Design Thinking” as a way to not only empathize and understand our users and their content, but as a means of changing our whole process, can lead to better outcomes for our users (and our clients and businesses) as well as radically change the way designers and developers collaborate. Learn how this change, this focus on users and collaboration, can help us navigate these strange and exciting times and build a better web.
HTML
1
star
67

slides-about-slides

Slides, about slides!
HTML
1
star