Description
By design most jquery code leads to a lot of tight coupling, e.g. selectors assume a specific structure of html
var mySubnav = $("#navigation a.sub-menu");
If the corresponding html changes, for whatever reasons,
<a class="subMenu" .... </a>
functionality is broken.
Question
What's the best way to handle tight coupling?
What approaches exist to loosen it up?
Answers, Approaches
use the html custom data attribute to separate css from js logic. e.g. add data-submenu="true" on the html and use var mySubnav = $("[data-submenu]"); on the js side.
implement a solid testing environment
couple as loose as possible, by using the least specific selectors, e.g. $("a.sub-menu'). See also
Eliminate the actual string literals that represent CSS selectors from the body of your jQuery code by (1) retrieving references to static DOM elements beforehand, and (2) storing selector strings in one place (at the top of your code).
use javascript frameworks, like Backbone, which decouple javascript from the DOM via views
use delegate and live regarding coupling due to event management
This may not be a popular answer, but... testing, testing, testing. JQuery and Javascript in general are typically tightly coupled, and for good reason; they're code running in the browser, and so you want to keep the performance relatively snappy. So injecting an indirection layer that allows for looser coupling can decrease performance; as well, it can be a bit of overkill, since there's typically a close pairing between the JQuery / Javascript code that's written and the pages they're written for; this is as much an artifact of the historical development of Javascript, but that's the way that it is. As such, the tight coupling is pretty "normal".
The way to deal with this tight coupling, like any tight coupling, is to make sure that you've got good testing in place to cover any coupling failures. Testing can provide assurance that the coupling is proper, and it's really the best way to assure the functionality you expect anyway.
One option is to use a Javascript framework, like Backbone. It gives you the concept of views which help decouple your Javascript from the DOM structure. For example, you can create the Comment view, which you assign to the following div:
<div class="comment">
<span class="title"></span>
<p class="body"></p>
</div>
And then you can access elements in the view relative to the comment div:
var title = this.$(".title");
This makes it easy to change the DOM structure outside of the comment div as long as the internals of the comment div remain the same.
Use Custom prefixed classes for UI hooks ui-specificGuy
Provided HTML
<div class="container grid8">
<ul class="ul1 horizontal">
<li>List Item 1</li>
<li>List Item 2</li>
</ul>
</div>
Bad - using style purposed classes/hooks
$('.container.grid8').stuff();
$('.ul1.horizontal').moreStuff();
Adjusting the HTML
<div class="container grid8 ui-specificContainer">
<ul class="ul1 horizontal ui-specificList">
<li>List Item 1</li>
<li>List Item 2</li>
</ul>
</div>
Good - using your own purposed classes/hooks
$('.ui-specificContainer').stuff();
$('.ui-specificList').moreStuff();
Only be as specific as neccessary
If this will accomplish your goal.
$('.ui-targetedElement')
Then why have a selector that looks like this?
$('ul > li a.ui-targetedElement')
This simply introduces unnecessary DOM structure dependencies into the functionality you are building, and you should be able to be proactive in this regard because you should be providing your own hooks (prefixed classes) at this point right?
Ultimately though I would say that tight coupling between the DOM and the script are sometimes unavoidable because of the nature of how they work together.
Full Article
If you are talking in respect to event management then make as much use of delegates and live which are not tightly coupled to the dom structure. Take a look at the below urls
Live -
http://api.jquery.com/live/
Delegate -
http://api.jquery.com/delegate/
My suggestion would be to:
retrieve the references of static DOM elements on DOM ready and put them in variables or properties of an object or whatever.
store class names inside an object (or several objects or whatever, as long as those names (strings) are in one place)
Then you can do this:
var mySubnav = $('a.' + C.submenu, navigation);
where navigation is a reference to the #navigation element and C is the class-names object which submenu property is the string "sub-menu".
Now, when you change class-names in your HTML code, you only have to update the C object.
The idea is to get rid of the actual string literals that represent CSS selectors from the body of your jQuery code by (1) retrieving references to static DOM elements beforehand, and (2) storing selector strings in one place (at the top of your code).
If your html changes, the all bets are off.
And what you have is not tight coupling. It is essential for your code to function.
E.g., this is what I would consider tight coupling:
<a class="subMenu" onclick="doSomething()"> .... </a>
The loose version would be:
$("#navigation a.sub-menu').click(function(){
//do something
});
What about using html5 data attributes to do javascript selectors?
<a data-submenu="true" .... </a>
var mySubnav = $("[data-submenu]");
Makes it really clear that javascript is operating on the html.
Related
I'm a front-end developer, and I'm worried about the best way to target my DOM.
Let's imagine a tiny form to create a new zombie :
<h1>Add a new zombie</h1>
<form id="create-zombie">
<input id="zombie" type="text" name="zombie" />
<input id="lvl" type="text" name="lvl" />
<button type="submit">Add</button>
</form>
...and if I want to get the values of zombie and lvl, I will code something like this:
class Zombie_Add extends Controller
# Dom References
el:
'form': '#create-zombie'
'zombie': '#zombie'
'lvl': '#lvl'
run: ->
#on 'submit', #el.form, #validate
validate: (e) =>
e.preventDefault()
zombie = $(#el.zombie).val()
lvl = $(#el.lvl).val()
module.exports = Zombie_Add
That's "ok" and it does the job, but I have some problems with that "structure" :
If somebody touches the DOM and removes an ID, I'm just fucked, it breaks my code (Captain Obvious spotted !)
For more complicated selectors, it's just a mess (I'm thinking about some stuff like that [name^="dummy-"] input:first). I guess it's easy to imagine how shitty the names of el are.
Anyway, what I want to learn today is what's the best way to target the DOM from JS. Is it better to use IDs, class values or, data-* attributes? How we can prettify a selector with plain English, etc...
If somebody touches the DOM and removes an ID, I'm just ****ed, it
breaks my code (Captain Obvious spotted !)
The best way to target a single, unique element in the DOM is with an ID like you are doing with zombie and lvl. It is both fast to execute and simple to code.
It is a given that if someone messes with the id values in your HTML, that will break your Javascript. That's just the way it is. Anyone messing with your HTML has to be smart enough to know that an ID value is there for a reason so if they want to change that or remove it, then it is their responsibility to find someone who can make corresponding changes in the Javascript. That's just the way it is.
For more complicated selectors, it's just a mess (I'm thinking about
some stuff like that [name^="dummy-"] input:first). I guess it's easy
to imagine how ****ty the names of el are.
The same goes for more complicated selectors. Like it or not, a modern web page is a melding of server-side stuff, presentation HTML and Javascript. The three have to work together to deliver the final solution. While you strive to avoid unnecessary dependencies with good design techniques and practices, you cannot avoid all dependencies, nor would even trying to avoid all possible dependencies be an efficient way to develop.
Example
There are coding practices that can make your code less sensitive to edits to the HTML and I consider those to be desirable practices. For example, consider this HTML snippet:
<div class="header">
<div class="headerBorder">
<div class="actions>
Hide
</div>
<div class="contentContainer">
<div class="content">
... content here
</div>
</div>
</div>
</div>
<div class="header">
... more of these repeated
</div>
And you want to have code that, when you click on the Hide link, it will hide the content.
If you code that like this:
$(".header .actions .hide").click(function() {
$(this).parent().next().children().first().hide();
});
Then, you have embedded in your Javascript some extremely detailed knowledge of the HTML structure in the area of your button and content and pretty much any structural change to that HTML (even just adding one more level of div to help with some layout) will break the Javascript. This is bad.
Instead, you could write this:
$(".header .actions .hide").click(function() {
$(this).closest(".header").find(".content").hide();
});
This depends only upon one HTML structural concept - that the .content that corresponds to the .hide button is somewhere in the common .header parent element. The entire structure within that .header parent can be changed and this code will not break. This is robust code that tries to be as independent of the details of the HTML structure as possible.
Anyway, what I want to learn today is what's the best way to target
the DOM from JS. Is it better to use IDs, class values or, data-*
attributes? How we can prettify a selector with plain English, etc...
It's best to use IDs to target elements for which there is only one unique element in the page.
It's best to use class names to target elements for which there can be more than one element in the page and you can combine that with other parts of a selector if you want to target only a specific item with a class name.
data attributes can be used in selectors, but that isn't really their primary purpose. I can't think of any reason why it would be better to use a data attribute instead of a class name. I use data attributes for storing actual data on an object that will be used by scripts. This tends to allow the code to be more generic and let the content describe itself.
When you talk about classes getting removed to change the state of the element, that is a legitimate use of a class, but it would just be a bad design decision to use the same class for selecting an element as for add/removing state. Use different class names for those two purposes. For example, you could have a class name called "selected" that indicates a selection state, but you would only use that in a selector if you wanted just the selected objects. If you wanted all line items in a table, you wouldn't use ".selected", you'd create a more descriptive class name for that object such as "lineitem". Remember, you can have multiple class names on an object so you can use different class names on the same object for different purposes.
You seem to be searching for some magic bullet here that prevents changes in the HTML from affecting Javascript in any way. That simply does not exist. Selecting a DOM element or finding a DOM element relative to something that was clicked will rely on some inherent knowledge of how the HTML is structured or tagged. What is important is that you minimize that dependence to only what is required and that anyone messing with the HTML has an understanding of how to best change things and not break the Javascript or discusses changes with someone who knows the Javascript. If you're using different skills/people on different portions of the project, they have to coordinate their efforts in order to not break things or be aware of what dependencies there are with the other parts of the system. That's just the way it is. There is no magic answer here that removes all dependencies between HTML and Javascript. Smart designs have smaller and more obvious dependencies. Poor designs have large and often hidden depedencies.
Your question about "plain English" isn't particularly clear to me. It seems obvious to use descriptive ID names, class names or attribute names that make it as obvious to the reader what that particular name is being used for. I'm not sure what you were looking for beyond that.
I'm making a project that includes a lot of elements creation and appending, after a little research i came up with that
$(newElement) and $(selector)
are slower than
.createElement(newElement) and .getElementBy
Since i'm doing a todo-list app which will include a lot of creation/appending/selection of this :
<div class="mainTaskWrapper clearfix">
<div class="mainMarker"></div>
<label for="task1">This is task1</label>
<div class="holder"></div>
<div class="subTrigger"></div>
<div class="checkButton"></div>
<div class="optTrigger"></div>
<div class="mainOptions">
<ul>
<li id="mainInfo">Details</li>
<li id="mainDivide">Divide</li>
<li id="mainEdit">Edit</li>
<li id="mainDelete">Delete</li>
</ul>
</div>
</div>
What would you advise me to use ? jQuery selection and creation way, or JavaScript one ?
When you say "a lot of creation/appending/selection", what exactly do you mean?
Is it in the order of several per second, or just "a lot"? Since the code will run client-side, one per second won't be that much of an issue.
jQuery would then be the 'best' choice, as maintainability is a large plus, and any code you write with jQuery will probably be a lot clearer.
Depends a bit on what else you're doing. In some cases, setting innerHTML is the fastest (esp. with big bulky blocks getting inserted).
That said from a dev standpoint the jquery method is really the most maintainable. I'd probably go w/ the jquery version until performance becomes a problem, and wrap the calls to jquery in other functions that I can swap out when it does.
It's really a developer preference issue.
Personally, I find strictly using jQuery selectors to be easier to write and understand, and more consistent when the selectors get more complex. Using native JavaScript is faster to compile and render, but you might not actually see any difference depending on the complexity of your page.
I would advise you NOT to create that many identical elements all at once. Instead, place them once in your HTML and hide it (use display:none) and then use jQuery to .clone() the whole block as needed.
HOWEVER: First, get rid of those id= attributes. You can't have more than one element on a page with the same ID.
Take, for example, the situation using knockoutjs:
<div id="container" data-bind="foreach:containers">
<li class="complex-element">...</li>
</div>
Now I want to attach some complex behavior to complex-element. How do I do that? I can't just attach events directly to complex-element, as they're created and removed dynamically at runtime to match the view model. So, as I see it:
I can riddle my html with data-bind="click:..."s and data-bind="mouseenter:..."s but I would rather avoid this if I could. Maybe I'm too rooted in my old MVC ways, but adding open(), select(), or dragStart() functions and isOpen, isSelected or isDragging observable flags to my view model just makes a mess and my intuition tells me that as the app gets bigger that view model is going to become unmanageable. I'd rather keep my data and my presentation separate if possible.
Or I can use jquery delegation to attach events to something that stays fixed. Something like:
$("#container").on('click', '.complex-element button.open', function(e) {
var elem = $(e.target).parents('.complex-element');
...
});
But this wouldn't work as the app gets more complex because what happens if the container is itself wrapped in an element only shown on login (<div id="wrapper" data-bind="if:isLoggedIn">...</div>). I might as well just bind all events to the body and that's a recipe for disaster.
I found a very cool article on knockmeout.net (http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html) that advocates using custom knockout bindings to drive complex behavior, and this seems to be an awesome solution for widget-like behaviors like autocompletes or date-pickers, but what about just simple old fashioned controllers... would this work?
I guess, after all that, my question is pretty simple: Has anyone used Knockoutjs on a really large web application? And how did you go about it?
You could use a different jquery bind:
$(document).on('click', '#container>.complex-element button.open', function(e) {
var elem = $(e.target).parents('.complex-element');
...
});
This would work if #container does not exist.
There is some work going on in the community to make the knockout bindings unobtrusive and easy to write. For now, knockout offers you dataFor() which can be seen here: Using unobtrusive event handlers or you can use a little library like this: Introducing the Knockout.Unobtrusive Plugin
If you need to execute some arbitrary js on some rendered elements from the foreach you could use the afterAdd callback
<div id="container" data-bind="foreach: { data: containers,
afterAdd: somefunction }">
<li class="complex-element">...</li>
</div>
I have used KO on a really large web app and the way I went about managing complexity was
Split out viewmodels into separate using namespaces within a single master namespace.
Write custom bindings for complex reusable behavior.
Created entity model classes that encapsulated most of the important business logic, akin to backbone models.
Ensured that the viewModels were entirely view specific and view related (helped by point 3)
Kept all jquery selectors to a bare minimum, I feel dirty writing any global delegates but sometimes they are needed.
Hope this helps.
A phenomena I'm seeing more and more of is Javascript code that is tied to a particular element on a particular page, rather than being tied to kinds of elements or UI patterns.
For example, say we had a couple of animated menus on a page:
<ul id="top-navigation">
...
</ul>
<!-- ... -->
<ul id="product-list">
...
</ul>
These two menus might exist on the same page or on different pages, and some pages mightn't have any menus.
I'll often see Javascript code like this (for these examples, I'm using jQuery):
$(document).ready(function() {
$('ul#top-navigation').dropdownMenu();
$('ul#product-selector').dropdownMenu();
});
Notice the problem?
The Javascript is tightly coupled to particular instances of a UI pattern rather than the UI pattern itself.
Now wouldn't it be so much simpler (and cleaner) to do this instead? -
$(document).ready(function() {
$('ul.dropdown-menu').dropdownMenu();
});
Then we can put the 'dropdown-menu' class on our lists like so:
<ul id="top-navigation" class="dropdown-menu">
...
</ul>
<!-- ... -->
<ul id="product-list" class="dropdown-menu">
...
</ul>
This way of doing things would have the following benefits:
Simpler Javascript - we only need to attach once to the class.
We avoid looking for specific instances that mightn't exist on a given page.
If we remove an element, we don't need to hunt through the Javascript to find the attach code for that element.
I believe techniques similar to this were pioneered by certain articles on alistapart.com.
I'm amazed these simple techniques still haven't gained widespread adoption, and I still see 'best-practice' code-samples and Javascript frameworks referring directly to UI instances rather than UI patterns.
Is there any reason for this? Is there some big disadvantage to the technique I just described that I'm unaware of?
First of all I agree with you that using the class approach is better, in general.
But I don't think I'd go so far as to say it's less coupling of the code to the UI. If you think about it, if the code assumes ID "foo" vs. class name "foo", you still have to know that when working with the UI. There's still a 'contract' between them -- whether you meet it through ID or class is not really different.
One disadvantage to using the class approach I'd imagine is speed -- it should be faster to find a particular element by ID than find potentially multiple elements by class. The difference is probably completely negligible though.
But, in the case where your code is designed to attach multiple behaviors, as in your two-dropdown example, using class certainly makes more sense. That is less coupling since your code is a bit more generalized, and your UI more likely to be customizable w/o changing the code.
One thing I'd change in both of your examples... why have the UL in the selector? If the code knows it can only possibly work if the target is a UL, well, that's one thing -- but in that case, it'd be better to avoid the UL in the selector and let the code throw a meaningful error if the target is found not to be a UL, lest the page just do nothing without any indication as to why (e.g. because the UI put the ID/class on a OL).
So in other words, just "#foo" or ".foo" not "ul.foo", etc.
I should point out that in case someone thinks the UL somehow makes the selector more efficient, it doesn't, since selectors are evaluated from right to left.
Your approach is preferred.
The reason people do things in different ways is because they can and it still works.
I found that quite a few "toolbar" in web page is implemented with HTML tag UL and LI with style "float:left".
Fore example, with the help of FireBug it is easy to find this pattern in http://www.yahoo.com/.
Is there any reason for that? I don't think that UL and LI are invented to create toolbar.
HTML was intended for semantics (what things mean), not presentation (what they look like). Since <ul> represents an unordered list, and since a toolbar is conceptually just a list of items, this is sensible. Even StackOverflow does it!
<div class="nav">
<ul>
<li>Questions</li>
<li>Tags</li>
<li>Users</li>
<li>Badges</li>
<li>Unanswered</li>
</ul>
</div>
UL and LI are for lists. And you are listing a bunch of links (sub-navigation, tools(?), etc.)
One possible reason - they have reasonable fallback behaviour one low-grade clients - i.e. it becomes a tree. But in reality <ul> and <li> really just describe nested list data; with css controlling the layout, their original intent is, largely, just a convenience.
Probably because someone who believes web standards are the one true way marked them up as such. After all, a toolbar is a list of icons!
But my personal approach to markup is that when you're creating a web app that involves some app UI element like a toolbar, you shouldn't necessarily have to build everything according to HTML web standards since they weren't created for marking up applications, but for documents. In which case, just use whatever works best for you (like a div with a list of spans, using classnames to specify contextual semantics for your app).
Note: my answer refers specifically to the use of toolbars, not regular navigation in websites.
One reason not yet mentioned is that writing menus as lists helps search engines parse your site better. Top of the line search engines will be able to figure out nested divs and even tables (in some cases) but it's always better to make the crawler's job easier to avoid possible confusion.
EDIT:
Links as requested:
http://blog.fryewiles.com/seo/03-04-2008/ul-titles-nesting-uls-for-better-seo
http://importantseotips.blogspot.com/2008/09/why-using-div-is-better-than-table-seo.html
http://webdev.entheosweb.com/2008/02/24/why-i-switched-to-a-tableless-design/
I actually couldn't find many articles specifically addressing ul tags for SEO, so I will have to qualify my answer by declaring my opinion.
I am of the opinion that using an unordered list to present menuing will assist crawlers in correctly parsing and indexing your site. Generally, well organized data (such as a list) lends itself to better and speedier crawlability. Organizing your data well will significantly improve the likelihood of a bot correctly finding your keywords, and ranking your site.
(Now if only my opinion on the subject were as valuable as Larry Page's. ;-) )
By allowing use of border-left/right, it also avoids the practice of forcing unsemantic separators (e.g. vertical bars) into your code for presentation purposes, which also adds unnecessary content for screen readers to read out. e.g.
<div class="nav">
Questions |
Tags |
Users |
Badges |
Unanswered
</div>