Element selectors for a RepeatButtonView I have a large application with a wide-reaching amount of views and sub-views. I have a need to decorate DOM elements with identifiers so that my framework of choice can bind views to the DOM elements.

In attempting to choose a robust convention for naming these selectors, I've encountered a few problems:


id selectors: it's difficult to maintain unique ID selectors across a large amount of views. Prefixing IDs can help alleviate this issue, but is overly verbose.
class selectors: normally classes are used just for CSS decoration. This CSS best practices guide suggests prefixing JavaScript-only CSS classes with js- to indicate there is no CSS decoration. This helps alleviate the issue, but can lead to confusing/error-prone code due to a mixture of js- and unprefixed classes.
data selectors: I can't see any reason to not use these for this purpose aside from being very non-standard. I haven't seen this used in any production code I've looked at.


Here are examples of the three scenarios above using Backbone.Marionette. The examples should be simple enough for the framework to not matter, though.

Does anyone have additional experience/opinions to contribute to the matter? Anything incorrect about the data selector route?

id selector

Too verbose and doesn't work on collections:

var RepeatButtonView = Marionette.ItemView.extend({
    id: 'repeatButton',
    template: _.template('&lt;div id=&lt;%= viewId %&gt;-repeatSingleIcon&gt;&lt;/div&gt;&lt;div id=&lt;%= viewId %&gt;-repeatAllIcon&gt;&lt;/div&gt;'), 
    templateHelpers: function(){
        return {
            viewId: this.id
        };
    },

    ui: function(){
        return {
           repeatSingleIcon: '#' + this.id + '-repeatSingleIcon',
           repeatAllIcon: '#' + this.id + '-repeatAllIcon'
        };
    }
});


class selector

use js- prefix to indicate class is only used for JavaScript. Potentially weird UI declaration when some classes have CSS.

var RepeatButtonView = Marionette.ItemView.extend({
    id: 'repeatButton',
    template: _.template('&lt;div class="js-repeatSingleIcon"&gt;&lt;/div&gt;&lt;div class="repeatAllIcon"&gt;&lt;/div&gt;'), 
    ui: {
        repeatSingleIcon: '.js-repeatSingleIcon',
        repeatAllIcon: '.repeatAllIcon'
    }
});

.repeatAllIcon {
    pointer-events: all;
}


data selector

potentially confusing due to rarity of implementation

var RepeatButtonView = Marionette.ItemView.extend({
    id: 'repeatButton',
    template: _.template('&lt;div data-ui="repeatSingleIcon"&gt;&lt;/div&gt;&lt;div data-ui="repeatAllIcon"&gt;&lt;/div&gt;'), 
    ui: {
        repeatSingleIcon: '[data-ui*=repeatSingleIcon]',
        repeatAllIcon: '[data-ui*=repeatAllIcon]'
    }
});
Using data-* attributes is actually part of the HTML5 standard.

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.
These attributes are not intended for use by software that is independent of the site that uses the attributes.

(emphasis mine)
In my opinion, I like to keep behavior separate from style, so I lean towards using data-* attributes. A problem arises when you attach CSS styles to those custom attributes, though I must admit that some styling is OK (for instance, showing or hiding elements). If you want a data-ui=child element styled a certain way, add a class attribute to it to maintain your separation of style and behavior.
My only piece of advice when using data attributes is to &quot;namespace&quot; them such that you have data-ui and more specific or related attributes are data-ui-something where each data attribute your view framework uses is prefixed with data-ui. This helps prevent naming collisions with other people's code.