Proposal
Don't use classes as selectors in product.js
, but data attributes instead.
Alternatively, prefix these selectors with js-*
as they are being used by a script and not a stylesheet.
Why
- Ship Slate with no generic prescribe classes in
product.liquid
that will likely conflict with vendor CSS.
- If we were to ship classes, I'd rather we encourage BEM syntax (or an equivalent). Otherwise, I am going to be re-writing this class selectors every project I start.
- Alternatively, a
js-*
prefix would also address the need to rewrite these selectors.
Looking to discuss with @Shopify/themes-fed
Background
I'm porting an existing commerce theme to use Slate. While setting up the product.liquid
section file, I noticed that the markup isn't using BEM. Example:
<div class="price-wrapper">
<span class="product-price">{{ current_variant.price | money }}</span>
</div>
...
<button type="submit" name="add" class="add-to-cart">
<span class="add-to-cart-text">{{ 'products.product.add_to_cart' | t }}</span>
</button>
A number of generic classes were already in use by my theme on different elements, so I had to change what Slate shipped. As a by-product, I also had to make modifications in the product.js
file's selectors
object:
var selectors = {
addToCart: '.add-to-cart',
addToCartText: '.add-to-cart-text',
// etc.
};
All of which raised the question: Do we need to use class selectors in our JS?
Proposal
I'm proposing we use data-attributes to tell our scripts what different components actually are, without necessarily giving the data attribute a value. Example:
<span data-product-price>
{{ current_variant.price | money }}
</span>
W3C references
I know that there are opinions that data attributes should be used only when you have actual information you want to store. Example: Setting the slide speed with data-slide-speed="500"
. Everywhere I've looked, those that feel you shouldn't use a data-attribute with no value reference the W3C spec:
Custom data attributes are intended to store custom data private to the page or application, for which there are no more appropriate attributes or elements
Source: http://w3c.github.io/html/dom.html#embedding-custom-non-visible-data-with-the-data-attributes
But W3C... I feel a class isn't an appropriate selector. Coupling JS behaviour to classes and page style is a risk I don't feel is appropriate. ... Now what, W3C?
Comparison framework: Foundation
I took a look at what the Foundation framework is doing, and they use an implementation that seems to line up with my proposal.
Classes are used to manipulate styles. As much as possible, Foundation avoids using classes to indicate things to JavaScript, and also avoids directly applying CSS attributes with JavaScript, preferring to add modifier classes as needed.
Data attributes are used for communicating with the Foundation JavaScript. These are what tell Foundation what type of component something is, and what types of options to configure it with.
Source: Control Your Behavior with Foundation’s Data Attributes
In the example from that blog, Foundation will use a standalone data-attribute
like data-slider-handle
in order to tell the JS where the handle is and let class take care of styling.
<div id="example-slider" class="slider" data-slider data-initial-start="50" data-end="200">
<span class="slider-handle" data-slider-handle role="slider" tabindex="1"></span>
<span class="slider-fill" data-slider-fill></span>
<input type="hidden">
</div>
Implementation for foundation.slider.js
in their repo: https://github.com/zurb/foundation-sites/blob/develop/js/foundation.slider.js#L55
Quick note: Bootstrap
I looked into Bootstrap, and they appear to opt for classes and only use data-attributes in their selectors when they need their values. Example: carousel.js selectors. However, I feel Bootstrap's framework is too opinionated to be compared to what we are trying to do with Slate.
Selector speed and alternative opinions
I am aware that selector speed is brought up a lot in favour of classes. That using classes to read through the whole DOM is faster than data-attributes. For that, I suggest reading this article: Don’t use data attributes to find HTML elements with JS.
The author brings up this speed argument, but more importantly for this issue the author would state what we are doing with our current classes isn't a good idea. The takeaway being that you should prefix your selectors with js-*
. ... Which I can get on board with if we were to ignore the wisdom of Foundation.
More performance stuff: From reading around the 'net, the data-attribute selector speed is often quoted as only being a problem in very large applications with nodes in the thousands. And in the comments of that blog post, a commenter (Chris Rivera) says all of the speed concerns can be side-stepped by properly caching your selector and scoping future look ups. i.e.
var $module = $(“[data-module=Cars]”);
var $list = $module.find(‘[data-cars]’);
^ This is effectively what Foundation is doing.