Security News
vlt Debuts New JavaScript Package Manager and Serverless Registry at NodeConf EU
vlt introduced its new package manager and a serverless registry this week, innovating in a space where npm has stagnated.
Element queries for modular responsive components.
If you want to use ELQ in a React project. Try the React wrapper called react-responsive-block.
File size is a bit big at the moment (237 KB, 16 KB minified + gzip). In the future, we will remove the lodash dependency, reducing the size to something like (70 KB, 6 KB minified + gzip).
Big thanks to Evry sponsoring the project.
Element queries are not for the faint hearted (read: not suitable for all projects). If your web app consists of modules that are (or should be) responsive – then this is most probably something for you! Otherwise, move on and come back another time :)
Since element queries have not been standardized (and not implemented in browsers), developers need to resort to JavaScript to provide element queries. There are numerous libraries that enable element queries in different ways, but here are the main selling points to why we believe ELQ is the best solution:
For a very detailed description of ELQ and element queries, read the Master's Thesis. The API and architecture is somewhat outdated, but the Thesis is still relevant for understanding ELQ.
We haven't had time to write proper docs yet. What follows is really a minimal explanation of the API.
Once the dist/elq.js
file is included into the HTML (it is built with UMD, so include it as you wish) it exposes a global function Elq
. This is a constructor that creates ELQ instances. It is recommended to only use one instance per application.
// Creating an ELQ instance is easy!
var elq = Elq();
// It also accepts an options object.
var elq = Elq({
cycleDetection: false
});
The main idea is to annotate elements with breakpoints of interest so that children can be conditionally styled in CSS by targeting the different breakpoint states. ELQ is bundled with three plugins as default, that let you annotate breakpoints as attributes of your elements like so:
<div class="foo" elq elq-breakpoints elq-breakpoints-widths="300 500">
<p>When in doubt, mumble.</p>
</div>
When ELQ has processed the element, it will always have two classes, one for each breakpoint, that tells if the size of the element is greater or lesser than each breakpoint. For instance, if the element is 400 pixels wide, the element has the two classes elq-min-width-300px
and elq-max-width-500px
. Similarly, if the element is 200 pixels wide the element the classes are instead elq-max-width-300px
and elq-max-width-500px
. So for each breakpoint only the min/max
part changes.
It may seem alien that the classes describe that the width of the element is both maximum 300 and 500 pixels. This is because we have taken a user-centric approach, so that when using the classes in CSS the API is similar to media queries. However, developers are free to change this API at will as elq
is plugin-based.
Now that we have defined the breakpoints of the element, we can conditionally style it by using the classes:
.foo.elq-min-width-300px.elq-max-width-500px {
background: green;
}
.foo.elq-min-width-500px {
background: blue;
}
.foo.elq-max-width-500px p {
color: white;
}
This API is sufficient for applications that do not need nested breakpoint elements, and similar features is provided by related libraries. However, using such API in responsive modules still limits the reusability since the modules then may not exist in an outer responsive context.
The reason this API is not sufficient for nested modules is because there is no way to limit the CSS matching search of the selectors. The selector .foo.elq-max-width-500px p
specifies that all paragraph elements should have white text if any ancestor breakpoints element is above 500 pixels wide. Since the ancestor selector may match elements outside of the module, such selectors are dangerous to use in the context of responsive modules. The problem may be somewhat reduced by more specific selectors and such, but it cannot be fully solved for arbitrary styling.
To solve this problem, we provide a plugin that let us define elements to "mirror" the breakpoints classes of the nearest ancestor breakpoints element. Then, the conditional style of the mirror element may be written as a combinatory selector that is relative to the nearest ancestor breakpoints element.
Let's expand our example HTML to include to nested modules, that uses the mirror
API.
<div class="foo" elq elq-breakpoints elq-breakpoints-widths="300 500">
<div class="foo" elq elq-breakpoints elq-breakpoints-widths="300 500">
<p elq elq-mirror>When in doubt, mumble.</p>
</div>
<p elq elq-mirror>When in doubt, mumble.</p>
</div>
As the paragraph elements are mirroring the nearest .foo
ancestor, we can now write CSS in the following encapsulated way:
.foo {
/* So that the nested module behaves differently */
width: 50%;
}
.foo p.elq-max-width-500px {
color: white;
}
Since we in the previous examples have annotated elements manually, the power and flexibility of the API have not been properly displayed. Only when combined with JavaScript, things get more interesting. To be continued...
An ELQ instance exposes the following public methods:
Returns the version of instance.
Registers a plugin to be used by the instance. Parameter is required to be a plugin definition object.
Returns the registered plugin instance.
Tells if the given plugin has been registered or not. Parameter can be a plugin name (of type string
) or a plugin definition object.
Returns a boolean
.
Activates the given collection of elements. This triggers ELQ to perform its work so that all element queries are updated. Plugins are invoked, so that they can perform their logic. Resize listeners are also installed when propriate. Elements may be activated multiple times.
Parameter can be any collection of elements, and also accepts a single element. The are assumed to be ELQ-elements and it is not recommended to activate non-ELQ elements.
Deactivates the given collection of elements. This stops all ELQ and plugin systems started by activation.
Registers a callback to be called for an event of an element. The element parameter is option, and if omitted the callback will be called when the event is emitted for any element.
Type: Boolean
Default: true
When enabled, the cycle detection system tries to detect cyclic rules and breaks them if needed. When a cycle is detected, a console warning is printed. This may be helpful during development to catch cycles.
Type String
Valid values: "px"
"em"
"rem"
Default: "px"
Sets the default unit for all breakpoints that do not have a unit postfix. For instance, if defaultUnit is set to "em"
, the breakpoint 300
is interpreted as 300em
while breakpoint 500px
is still interpreted as 500px
.
One our of contributions is to allow ELQ to be easily extended with plugins. For example, if annotating HTML is undesired it is possible to create a plugin that instead parses CSS. Likewise, if adding breakpoint classes to element is undesired it is possible to create a plugin that does something else when a breakpoint state of an element has changed. In order to enable such powerful behavior altering by plugins, extensibility has been the main focus when designing the ELQ architecture.
A plugin is defined by a plugin definition object and has the following structure:
var myPluginDefinition = {
getName: function () {
return "my-plugin";
},
getVersion: function () {
return "0.0.0";
},
isCompatible: function (elq) {
return true;
},
make: function (elq, options) {
return {};
}
};
All of the methods are invoked when registered to an ELQ instance.
The getName
and getVersion
methods tells the name and version of the plugin.
The isCompatible
tells if the plugin is compatible with the ELQ instance that it is registered to.
In the make
method the plugin may initialize itself to the ELQ instance and return an object that defines the API accessible by ELQ and other plugins.
When necessary, ELQ invokes certain methods of the plugin API, if implemented, to let plugins decide the behavior of the system. Those methods are the following:
activate(element)
: Called when an element is requested to be activated, in order for plugins to initialize listeners and element properties.getElements(element)
: Called in order to let plugins reveal extra elements to be activated in addition to the given element.getBreakpoints(element)
: Called to retrieve the current breakpoints of an element.applyBreakpointStates(element, breakpointStates)
: Called to apply the given breakpoint states of an element.In addition, plugins may also listen to the following ELQ events:
resize(element)
: Emitted when an ELQ element has changed size.breakpointStatesChanged(element, breakpointStates)
: Emitted when an element has changed breakpoint states (e.g., when the width of an element changed from being narrower than a breakpoint to being wider).There are two main flows of the ELQ system; activating an element and updating an element. When ELQ is requested to activate an element, the following flow occurs:
getElements
method of all plugins is called to retrieve any additional elements to activate. Additional elements will go through an own flow.activate
method of all plugins is called so that plugin specific initialized may occur.The update flow is as follows:
getBreakpoints
method of all plugins is called to retrieve all breakpoints of the element.applyBreakpoints
method of all plugins is called.breakpointStatesChanged
event is emitted.Of course, there are options to disable some of the steps such as cycle detection and applying breakpoints. In addition to being triggered by the start flow and plugins, it is also triggered by element resize events.
The API that enables developers to annotate breakpoints in HTML, as described in the usage section, is implemented as two plugins. One plugin parses the breakpoints of the element attributes and one plugin applies the breakpoint classes. The simplified code of the make
method of the parsing plugin is as following:
function activate(element) {
if (!element.hasAttribute("elq-breakpoints")) {
return;
}
element.elq.resizeDetection = true;
element.elq.updateBreakpoints = true;
element.elq.applyBreakpoints = true;
element.elq.cycleCheck = true;
}
function getBreakpoints(element) {
return ...
}
// Return the plugin API
return {
activate: activate,
getBreakpoints: getBreakpoints
};
The applying plugin simply implements the applyBreakpoints
method to alter the className
property of the element by the given breakpoint states.
FAQs
Element queries library. Solution to modular responsive components.
The npm package elq receives a total of 1,162 weekly downloads. As such, elq popularity was classified as popular.
We found that elq demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
vlt introduced its new package manager and a serverless registry this week, innovating in a space where npm has stagnated.
Security News
Research
The Socket Research Team uncovered a malicious Python package typosquatting the popular 'fabric' SSH library, silently exfiltrating AWS credentials from unsuspecting developers.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.