A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

Extending .classList methods to allow RegExp

briankardell
2014-09-02

Ana Tudor shared a thought on Twitter this morning that she was thinking it would be handy if .contains and .remove could take a RegExp - as in:

element.classList.contains(/_foobar/)

This seems reasonable, handy and, I think actually prollyfillable by simply capturing the call, checking if it is a RegExp and if so, basing it off of the .className property which contains all of it as text… Any thoughts?

tabatkins
2014-09-02

I presume this would be for .contains() and .remove(), right? Obviously not .add(). :slight_smile:

I don’t see a problem with it, but I’m also not sure of its great value. I wouldn’t oppose it.

briankardell
2014-09-02

I’d suggest it’d make a good unofficial draft with a prollyfill - only the dev community is a good judge of it’s usefulness. I’m way tied up at the moment, anyone want to do it? Github FTW?

claudiopro
2014-09-02

.toggle(/_foobar/) could work by removing all matching classnames and returning them as an array, thus allowing the caller to toggle (i.e. add) them again later:

var c = element.classList.toggle(/_foobar/)
> _foobar, _foobarHover
c.forEach(element.classList.toggle, element.classList)

BTW, just tried this on Chrome which adds a literal class="/_foobar/", is this expected behavior?

tabatkins
2014-09-02

That is the expected behavior given the current definitions, yes, because it stringifies its argument, and the stringification of a regex the regex itself, as a string.

briankardell
2014-09-02

This would change the signature of toggle which returns boolean, an empty array isn’t the same as false even with coercion, right?

I’m not sure we could really make that work without potentially breaking something.

feeela
2014-09-03

This is already possible using element.className as this property is a string. As far as I understood it, the intention of element.classList is to provide an alternate way besides a regex search on a string to access an elements class names.

I don’t see the benefit of repeating an already existing behavior on yet another object property.

// A contains() example:
element.className.match(/_foobar/);

See: https://developer.mozilla.org/en-US/docs/Web/API/Element.className

briankardell
2014-09-03

Yeah, I think I said it was possible and should be prollyfillable above… I think that the ask was about ergonomics and developer expectations - if developers minds tend to think that contains should accept a regexp and that is easy to accommodate then it’s actually worth it. The only way to really know is to write it up and throw out a prollyfill that sugars it up as it would work and then see if it gets popular.

I’d like to point out, just for the sake of argument, that classList methods actually add no real new capabilities, all methods were established in libraries in a manner similar to the above and it was their popularity and better ergonomics that led to their addition. I agree that this is pretty one to one, but there is literally no harm in someone writing a prollyfill and draft.

feeela
2014-09-03

These methods are finally native implementations of what was possible with several JS libraries before. They are much faster than any JS polyfill. Using yet another regexp logic in a method like contain would decelerate their performance. As far as I can see it, this is primarily a performance issue. In most cases you don’t need the capabilities of a regular expression to find an element by its class name.

Firefox 3.6 Benchmarks

briankardell
2014-09-03

Ok, I don’t want to beat a dead horse here - but I don’t think that performance is why we did classList as much as ergonomics. The perf stuff you link to is measuring thousands of operations. Of course it’s faster, and good. And if you simply delegate to the native in the case that it is not a regexp, yes it will still be slower, but it’s actually very close - you can measure it yourself. And it would be about the same price for a regexp of doing tests on classname… And of course, if it was popular and ergonomic, that would become native too… So - I don’t see a perf argument to be had here, personally. I think we can agree to disagree if necessary though.

FremyCompany
2014-09-04

(+1) I think if your intent is to work with a regular expression, you’d better work with a string and not an array.

jonathantneal
2014-09-05
(function (prototype, slice, nativeContains, nativeRemove) {
	function contains(token) {
		if (typeof token === 'object' && typeof token.test === 'function') {
			return slice.call(this).filter(function (item) {
				return token.test(item);
			});
		}

		return nativeContains.apply(this, arguments);
	}

	function remove(token) {
		if (typeof token === 'object' && typeof token.test === 'function') {
			return slice.call(this).filter(function (item) {
				return token.test(item) && !nativeRemove.call(this, item);
			}, this);
		}

		return nativeRemove.apply(this, arguments);
	}

	nativeContains = prototype.contains;
	nativeRemove = prototype.remove;

	prototype.contains = contains;
	prototype.remove = remove;
})(DOMTokenList.prototype, Array.prototype.slice);

Something like this? If there’s actually any demand for this, consider it CC0, and I’ll throw it on GitHub. My feature-creep brain says this is just one step away from accepting your own methods.