29

It's very common to see Javascript bound to certain selectors to find elements, store data, and listen for events. It's also common to see these same selectors used for styling.

jQuery (and its selector engine Sizzle) support and promote this by referencing elements with CSS-type syntax. As such, this technique is particularly difficult to 'unlearn' (or refactor) when building out projects.

I've come to understand that this is a result of the history of HTML and Javascript development, and that browsers have been built to efficiently consume / parse / and render this sort of coupling. But as websites become increasingly complex, this reality can introduce difficulties in organizing and maintaining these separate layers.

My question is: can and should this be avoided in modern websites?

If I'm new to front-end development, and I wish to learn things 'the right way,' is it worth learning to decouple and avoid such dependencies from the start? Does this mean avoiding jQuery in favor of a library that promotes a more decoupled structure?

3
  • 1
    How would such a thing work? How do you, for example, disable a control on a page without actually touching that control in some way, or having knowledge of it? (this is my little way of saying that you need to be more specific about what you mean by decoupling, ideally with some examples, even if they are contrived). Mar 27, 2014 at 17:43
  • 2
    The most important thing when you talk about decoupling html, css and js is use class selectors instead any other, this is the core concept of methodologies like oocss and BEM.
    – angvillar
    Mar 27, 2014 at 18:08
  • Learn React or web components, and you won't have to bother with selectors in JS anymore.
    – Andy
    Jan 27, 2016 at 10:17

5 Answers 5

35
+50

There is no way to avoid that. They are coupled because they interact with each other. If your javascript intends on doing any kind of DOM manipulation, then it needs a way to reference the DOM.

There are various conventions for it.

The Level 2 DOM API provides the getElementById, getElementByTagName, and getElementsByName methods. To this day these are the workhorses of any kind of DOM traversal. All other fancier jQuery selectors resolve into a combination of these eventually, and/or straight up traversal of each DOM node (this was the way to do getByClassName).

There is no other shortcut. Javascript needs to know what to do and where. Typically, if an element has an id or class that is only relevant in scripting, a lot of people will prefix it with js- or some other obvious flag.

Another newer convention is the data-attribute selection.

<ul data-myapp-sortable="true">

jQuery('[data-myapp-sortable]').makeSortable();

The data-attribute is generally used for scripting purposes and selecting using that makes some sense. The drawback is that this is slower than using getElementById().

Another approach is the one used by angularJS, which creates a view-model. In this convention any kind of scripting functionality is specified by specially designated attributes like ng-disabled ng-href and many more. You don't add selectors in your javascript. The HTML document becomes the main authority on what is scripted and how, and the javascript works on it abstractly. It's a good approach, but obviously with a higher learning curve than the previous methods. And again, performance has to be considered.

But don't ever think that you can write interactive HTML and javascript, and somehow have both those parts not know about the other. It's more about how can you limit the references to dependencies.

3
  • 2
    Brilliant answer, +1. If only for mentioning data attributes as a mechanism for avoiding tight coupling Mar 27, 2014 at 20:35
  • 3
    data-attributes are not a panacea. They're very popular these days and people are putting everything but the kitchen sink in them. A lot of frameworks (e.g. jQuery UI) use them extensively. You have to be real strict with namespacing and other conventions to avoid problems. They help decouple HTML from JS, but they don't necessarily make debugging any easier. Mar 27, 2014 at 21:01
  • I've never understood why using classes, IDs, and data attributes as hooks and state flags needed to be reinvented. All Angular has accomplished in that regard is a reduction in performance and replaced widely known/understood convention with a new one requiring one understand how to do it "the Angular way," inventing your own attributes and tags. There's no huge learning curve there. It's just slower, counter to perfectly reasonable and commonly known convention and completely unnecessary IMO. Jul 5, 2015 at 23:46
9

If you're willing to forego the interactivity that you get, you can avoid Javascript entirely. Frameworks like ASP.NET MVC are very good at serving up pages that only contain HTML, CSS, and a SUBMIT button.

OK. Maybe that's a bit extreme.

Decoupling in a web application already occurs at many levels. REST applications allow you to define your application in terms of "web resources" that are associated with an URL. View Models allow you to present data to the UI that is decoupled from the domain model, but has the shape that you need to properly display it. Service layers allow one UI to be swapped for another, and so forth.

Historically, there's always been a tradeoff between interactivity and coupling. The more interactive your web page is, the more tightly-coupled to the application it's going to be. But the interactivity logic in the web page is confined to the web page itself; any coupling with the server is going to occur through either POST or AJAX. So I'm not sure you should be overly concerned with coupling at the Javascript level, other than paying attention to the way data packets are passed between the browser and the server.

The most "modern" approach (i.e. the flavor of the week) is probably SPA applications.

1
  • Doesn't sound extreme to me. Many sites which make extensive use of JavaScript, to the point that they're unusable without it, don't actually need it. Would that their developers had more of a clue... Mar 27, 2014 at 21:50
5

Martin Fowler describes one approach to this as the Segregated DOM, where you separate your DOM JavaScript from your page logic JavaScript.

Application Logic <----> DOM Manipulation <----> DOM / HTML
1
  • 1
    +1 I totally agree with segregating the JavaScript <--> DOM logic. I really don't like data attributes as these are not pertinent to the DOM but for an external tool. I feel that a cleaner approach is to have some type of mapping. Yes, this may mean that you then have a file with references to two aspects (eg, JS functions and DOM elements) rather than, eg, the DOM containing some references which the JS picks up (which might be described as 'single-aspect'). However, if done thoughtfully this can be very maintainable, reusable, and offer better separation of concerns than data attributes.
    – awj
    Mar 28, 2014 at 10:46
2

No, using class, element, and ID selectors client-side should not be avoided. The syntax is used because CSS selectors are very mature and well-established domain language, and having a common design makes it far easier to share a common logical model of a page between program and design, which is a very, very good thing.

While it is possible to mis-use this syntax and create a terrible and unmaintainable application, such is possible regardless of your language or toolset.

3
  • 2
    I actually recommend against using class, element, and ID selectors for many things, and instead focus on using custom [data-*] attribute selectors, which can be used in very powerful ways.
    – zzzzBov
    Mar 27, 2014 at 20:03
  • 2
    Poor advice in my mind, especially when it comes to writing modular/reusable JS that should make no assumption on selectors. Data attributes are a better idea for these scenarios. Mar 27, 2014 at 20:27
  • 3
    @zzzzBov - I know it's a microoptimization, but ID and class lookups are a lot faster than data-attribute lookups. But yes, I like the idea of using different sets of attributes to handle different concerns. Mar 27, 2014 at 22:35
0

Someone needs to build a jQuery path manager interface for an indirection and cache layer to the dom.

pathMgr.register(name,selector [,isDynamic=false]);
pathMgr.get(name [,refresh]);

Then,

String.prototype.reg([isDynamic=false]);
String.prototype.get(name [,refresh]);

So,

// at init....
var pathMgr=new PathMgr();
'sidebar-links #sidebar a'.reg();// new registery of selector '#sidebar a' under name 'sidebar-links'
// more, more


// in code
'sidebar-links'.get().css(etc...);
//or
'sidebar-links'.addStyleRule({});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.