Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
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). –  Robert Harvey Mar 27 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. –  Pitxon Mar 27 at 18:08

6 Answers 6

up vote 32 down vote accepted
+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.

share|improve this answer
2  
Brilliant answer, +1. If only for mentioning data attributes as a mechanism for avoiding tight coupling –  Fergus Morrow Mar 27 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. –  mastaBlasta Mar 27 at 21:01

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({});
share|improve this answer

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
share|improve this answer
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 at 10:46

As others have said, if you have JavaScript that interacts with the DOM, then there is going to be some coupling.

The question is about the tightness of coupling. Tight coupling between JavaScript, HTML and CSS typically happens when:

  • JavaScript functions run the same hardcoded DOM queries multiple times, rather than binding elements to aliases once and then passing elements to manipulate explicitly
  • DOM selectors are overly specific about HTML structure or misuse selectors for attributes that relate to different concerns (e.g. using $('div > p > a[accesskey]) because that happens to find the right element, not because you need a link with an accesskey in a paragraph)
  • JavaScript that applies visual styles assuming that certain CSS selectors already exist (I am a believer that presentational JS should apply all styles itself that are implied by its contract)

I'd argue that all these are solvable by proper design, aliasing queries and using explicit dependency injection rather than dumping jQuery (though arguably some MVC libraries like Angular would help solve this problem).

share|improve this answer

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.

share|improve this answer
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 at 20:03
1  
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. –  Fergus Morrow Mar 27 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. –  Jimmy Breck-McKye Mar 27 at 22:35

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.

share|improve this answer
    
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... –  Michael Hampton Mar 27 at 21:50

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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