It's possible to think of Element Queries as a natural extension to CSS Media Queries. I would summarize the requirements for media queries like this:
- a media type
- query condition(s)
- a stylesheet
In CSS this gets exposed as:
@media screen (min-width: 500px) {
html { background: lime; }
}
The @media
wraps the stylesheet, the screen
part is specifying the media type, and (min-width: 500px)
specifies the query condition(s).
In HTML the same functionality is exposed through the media=""
attribute on <style>
and <link>
tags:
<link rel="stylesheet" media="(min-width: 500px)" href="data:text/css,html { background: lime; }">
<style media="(min-width: 500px)">
html { background: lime; }
</style>
Finally, in JavaScript the media querying functionality is available to authors using the window.matchMedia
:
if (window.matchMedia('(min-width 500px)').matches) {
css = 'html { background: lime; }'
}
In order to extend these syntaxes in HTML, CSS, and JavaScript to cover the functionality needed to describe element queries, let's look at the requirements for element queries:
- a selector (list)
- query condition(s)
- event listener(s)
- a stylesheet
Here we have a couple new ideas: a selector to apply the stylesheet to, and event listeners.
Normally JavaScript events are abstracted away from CSS authors - you don't need to worry about window.load
or window.resize
when writing media queries, you don't need to worry about mouseover
or mouseout
when writing :hover
, and you're not worried about focus
or blur
when using :focus
, etc.
For the most part I believe browser makers will be able to find a way to implement element queries in the most general sense without the need for CSS authors to specify the specific events they should listen to. In addition, enhancements may be made by listening to specific events related to the selector, and/or query conditions. If a query condition for scroll is used, the browser should be able to automatically listen to scroll events on the tags matching the selector used.
However it is likely that performance improvements could be made by allowing authors to limit which events trigger a reevaluation of the query conditions using a whitelist.
Proposed CSS Syntax:
@element html {
html { background: lime; }
}
Here the @element
wraps the stylesheet, the html
specifies the selector, and we are lacking query conditions or events. This is a simple element query that would apply to the document as long as an <html>
element was found.
@element textarea (min-characters: 50) {
textarea { background: lime; }
}
This example includes a query condition that counts the number of characters of text inside the value
of a <textarea>
element.
This brings us naturally to the question: "What happens if there is more than one <textarea>
in the document?" It certainly looks like as long as any <textarea>
has more than 50 characters, all <textarea>
tags in the document will gain a style. Because of this, another concept is required: a scoped selector.
I like the idea of using $this
as a placeholder inside selectors in the element query stylesheet, to represent each element tested that passes the query conditions.
@element textarea (min-characters: 50) {
$this { background: lime; }
}
Now if we have multiple <textarea>
present in our document we have a way of targeting only those specific <textarea>
elements that match the query conditions. But the use of this placeholder isn't limited to just applying styles to matching elements themselves - it should be able to be used anywhere in selectors in the contained stylesheet:
@element .widget (max-width: 300px) {
$this h2 { font-size: 10pt; }
}
Lastly, a way to specify limited events should be offered to the user. This has no precedent in CSS so here are some ideas. It's possible that the specific names of events could be aliased to something new, but assuming for a moment that we are using the names of events from JavaScript, we need a way to specify both window
-level, and tag-level event listeners. Maybe something like:
@element textarea (max-characters: 5) on blur {
$this { background: red; }
}
I would hope that by default, based on this query applying to an input element it would naturally listen to events like blur
and focus
and reevaluate the query conditions when those events occur, but here we limit reevaluation only to the blur
event, and it is prevented from reevaluating any other time.
How could the same element query functionality be exposed to HTML? Perhaps through the use of additional attributes on <style>
and <link>
tags:
selector
for a selector listelement
for query conditionsevent
for events
These could look like the following:
<link rel="stylesheet" selector="textarea" element="(max-characters: 5)" event="blur" href="data:text/css,$this { background: lime; }">
<style selector="textarea" element="(max-characters: 5)" event="blur">
$this { background: lime; }
</style>
And to expose this functionality to JavaScript, perhaps something like window.matchMedia
could be useful, maybe window.matchElement
. It would work similarly to this:
document.querySelectorAll(<selector>).filter(<query conditions>)
So for an element query of (min-width: 500px)
on <div>
tags you would need a test like:
document.querySelectorAll('div').filter(el => el.offsetWidth > 500)
This works fine, but the real power of window.matchMedia
is that it's able to handle CSS units, rather than just working with pixels. Having the ability to do something like the following example would be better than what we have in JavaScript right now:
window.matchElement('div', '(min-width: 50em)')
If you're looking for syntax precedents for these ideas, or plugins that use something similar, check out: