No Description

Sven Slootweg 0a8411eb2c 1.0.0 4 years ago
src 8eeb22b7b8 Add documentation and changelog 4 years ago
.gitignore 349836fe2c Initial commit 4 years ago
.npmignore 0117e7cadc Create .npmignore file to ensure that lib/ will be included in the published module 4 years ago
CHANGELOG.md 8eeb22b7b8 Add documentation and changelog 4 years ago
README.md 8eeb22b7b8 Add documentation and changelog 4 years ago
gulpfile.js b083fa75df Switch to using Gulp presets 4 years ago
index.js 349836fe2c Initial commit 4 years ago
package.json 0a8411eb2c 1.0.0 4 years ago
test.js 349836fe2c Initial commit 4 years ago

README.md

riot-query

A module for easily traversing through a Riot.js tag hierarchy, using a simple custom query language. Can be used stand-alone or as a tag mixin.

Why you might need this

  • Riot.js is inconsistent in how it exposes its tags - it will be an array if there are multiple, and a single element if there's only one. This means you constantly have to normalize the results.
  • While selecting direct descendants is easy, it's a lot trickier when you need to search in the entire hierarchy, for example because you have a wrapper element that yields its content.

License

WTFPL or CC0, whichever you prefer. A donation and/or attribution are appreciated, but not required.

Donate

Maintaining open-source projects takes a lot of time, and the more donations I receive, the more time I can dedicate to open-source. If this module is useful to you, consider making a donation!

You can donate using Bitcoin, PayPal, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else. Thank you!

Contributing

Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the files in src/, not those in lib/.

Build tool of choice is gulp; simply run gulp while developing, and it will watch for changes.

Be aware that by making a pull request, you agree to release your modifications under the licenses stated above.

Query language

A query consists of two parts: a Riot query and a DOM query. Both are optional, but you must always specify at least one of them.

In the Riot part of a query, the structure always refers to the hierarchy of Riot tags, not other DOM elements. In other words, the hierarchy follows the one you'd find in this.tags. In the DOM part of the query, you can specify DOM elements to select in the set of Riot tags that you've selected.

The riot-query language is somewhat syntactically similar to shell expansion. Below is a reference chart:

Syntax Meaning
foo Each foo tag.
foo/bar Each bar tag that is a direct descendant of a foo tag.
foo/**/bar Each bar tag that is a child of a foo tag, anywhere in its subtree - it does not have to be a direct descendant.
{foo,bar} Each foo and bar tag
foo/{bar,baz} Each bar and baz tag that is a direct descendant of a foo tag.
foo/{bar,baz,} Each foo, foo/bar and foo/baz tag (note the last comma!)
foo//.someClass Each DOM element matching .someClass that exists somewhere within a foo tag.
foo//.someClass > .sub Each DOM element matching .someClass > .sub that exists somewhere within a foo tag.

These are only some simplified examples - any combination of the above is valid, as long as the (optional) DOM part of the query comes last. riot-query uses a real query parser, so there are no edge cases.

Usage

An example of a simple query:

menu-bar/{menu-button,menu-submenu}

This would select every menu-button and menu-submenu that is directly nested within a menu-bar.

An example of a contrived, complex query:

foo/bar/**/{baz,qux/quz,something/{hai,bai},}/{duck,swan}//.domFoo .domBar

This would select every DOM element matching .domFoo .domBar that exists within any of the following paths:

  • foo/bar/**/baz/duck
  • foo/bar/**/baz/swan
  • foo/bar/**/qux/quz/duck
  • foo/bar/**/qux/quz/swan
  • foo/bar/**/something/hai/duck
  • foo/bar/**/something/hai/swan
  • foo/bar/**/something/bai/duck
  • foo/bar/**/something/bai/swan
  • foo/bar/**/duck
  • foo/bar/**/swan

The last two might seem a little odd, but note the extra comma at the end of the first group - a trailing comma is considered to indicate an empty item, similar to how it works in Bash, and in riot-query that means that the entire segment is ignored.

An example of making a query using riot-query as a mixin:

<menu-bar>
	<menu-section>
		<menu-item>Text 1</menu-item>
		<menu-item>Text 2</menu-item>
	</menu-section>
	
	<script>
		this.mixin(require("riot-query").mixin);
		
		this.on("mount", () => {
			this.query("**/menu-item").forEach((menuItem) => {
				menuItem.on("clicked", () => {
					console.log("Clicked menu item", menuItem);
				});
			});
		});
	</script>
</menu-bar>

A similar example, but selecting DOM nodes rather than Riot tags:

<menu-bar>
	<menu-section>
		<menu-item>
			<i class="icon icon-house"></i>
			Text 1
		</menu-item>
		<menu-item>
			<i class="icon icon-keys"></i>
			Text 2
		</menu-item>
	</menu-section>
	
	<script>
		this.mixin(require("riot-query").mixin);
		
		this.on("mount", () => {
			this.query("**/menu-item//i.icon").forEach((icon) => {
				console.log("Icon classes", icon.classList);
			});
		});
	</script>
</menu-bar>

An example using riot-query stand-alone rather than as a mixin:

<menu-bar>
	<menu-section>
		<menu-item>
			<i class="icon icon-house"></i>
			Text 1
		</menu-item>
		<menu-item>
			<i class="icon icon-keys"></i>
			Text 2
		</menu-item>
	</menu-section>
	
	<script>
		const riotQuery = require("riot-query");
		
		this.on("mount", () => {
			riotQuery(this, "**/menu-item//i.icon").forEach((icon) => {
				console.log("Icon classes", icon.classList);
			});
		});
	</script>
</menu-bar>

API

riotQuery(tag, query)

Run a query on a given tag, and return the results.

  • tag: The Riot.js tag to query from. This cannot be a regular DOM element, it must be a tag.
  • query: The query to run, following the query syntax described above.

If the query contains a DOM query, this function will return DOM elements. If it doesn't, it will return Riot tags. If only a DOM query is specified without a Riot query (eg. //.someClass), then riot-query will search through the DOM tree for the tag that riotQuery was called on.

Keep in mind that if your query selects DOM nodes rather than Riot tags, they will be returned as an array, not as a NodeList. Additionally, DOM queries will also include DOM elements in child tags, not just those that are a part of the tag you're querying from.

riotQuery.one(tag, query)

The same as riotQuery, but it only ever returns the first result, without being wrapped in an array.

riotQuery.mixin

A mixin object that you can pass into the this.mixin method for any Riot tag.

Mixin API

this.query(query)

The same as riotQuery, but without the need to specify a tag - it will always apply to the current tag.

this.queryOne(query)

The same as riotQuery.one, but without the need to specify a tag.