How should I avoid duplicate IDs when writing JS-heavy web apps? - javascript

Specifically, I have areas of my (heavily JS) web page that contain lists of HTML elements and for simplicity let's assume there are just two lists: A and B. Let's also say When I click on the first item in list A (A1) something happens to the first item in list B (B1).
I gave each element in list A a common HTML class and an ID (like <li class='listAmember' id='1'>...</li>). I set up elements in list B in a similar way (like <li class='listBmember' id='1'>). Then I attached some click handlers (using jQuery in this case) and ran into problems with duplicate IDs, even if I qualified my jQuery id-based selectors with the class name, like this
$('.listAmember#1').click(function(){});
I've realised that this is bad practice (IDs are supposed to be unique in the document!) but I'm unsure what good alternatives there are.
I don't really want to create IDs like listAmember-1 because if I have, say, a lookup-table in my JS code that is keyed on the numeric ID, I'd need to parse out the 1 from that ID string to get the 'real' ID. (And I would still need to add a class like listAmember for styling purposes and that seems unnecessarily repetitious?) All in all that seems clumsy and I assume there are better ways?

As Florent says, I would try and avoid IDs altogether. One convention is to use class names like js-list-a to signify it's a class intended for JavaScript usage only. Then you're free to start using the index of the li within the list.
Also, you could just set one click handler on the list and use event delegation .
A combination of these ideas means you won't have to touch IDs.

For Unified ID's maybe in a System that has external Libarys in use, you should always attach them with a Namespace. For Example like: "myFancySlider-container"
This helps to keep names Unique for your special application.
If you generate Dynamic Code, you have to Make sure, to only create One of each object. Or you Assemble them With iterations. An alternative to that, could be the unifying with Timestamps.
EDIT
As others already mentioned, you should avoid Using ID's as you can. ID's only make sense, to benefit from the Unique Attributes. For example, you know exactly what element you want, so an ID can be a verry good shortcut for that. Specialy in dynamic Content Applications.

Related

what happens if one set id attribute values of several html tags, the same string?

I recently saw a html page, that i thought the id of several html tags was the same, then I realized the ids are unique but it raises the question that what could have happened if the page actually used several tags
As i have heard id attribute of every html tag(if it has one) must be unique,
now i wonder what happens if it is not the case??
what possible errors can it cause?
does different browsers show different reactions for this issue?
does javascript and jquery codes that use duplicated ids run on both tags or what?
Duplicate ids can have various different effects. Which you experience will depend on the method you use to try to access them (and possibly also from browser to browser).
You'll affect all of them
You'll affect the first one
You'll affect the last one
You'll get a collection instead of an element, try to treat it like an element and get an error
Duplicate ids are not allowed in HTML. Don't make trouble for yourself. Use classes for groups and ids for unique identifiers.
Change them as soon as possible, to save a lot of headache in the future. For elements with same property use classes
About your queries,
Now i wonder what happens if it is not the case??
Well, the HTML is not a valid one anymore. Now a days it doesn't hurt much but still not preferred.
What possible errors can it cause?
Errors are little bit hard to predict. But with jQuery you are going to get many.
Does different browsers show different reactions for this issue?
Not sure.
Does javascript and jquery codes that use duplicated ids run on both tags or what?
jQuery will give you trouble. Consider a case where you have two input fields with same ID.
and you try to select second one with out noticing. jQuery('yourID').val() and you'll be selecting the firs value instead. Like this there are a lot of possibilities.
As you said, HTML id, per specs, must be unique. If one where to put duplicated id, the js behavior relative to those ids will be unpredicatable, and could even change between 2 calls.
Any js call on one id (jquery or not) will point to one of the id but without guarentee that :
It will be the same every call
It will have the same order between 2 page refresh
It will have the same behavior beween 2 different browser
It will have the same behavior betwween 2 time the same browser
The problems that could emerge depend on how toghtly the js code is coupled to the underlying element DOM structure anw could mostly point to a undefined exception and stop the js execution.

Is there any proper way to reuse Id's?

I have a "create" form inside a bootstrap's modal. All my Javascript related with that form uses id's and not classes. Now what i want to do is to create a second form that uses the same Javascript (based on id's as said before). Is there any proper way to achieve that? I know that is a very bad practice to use dublicate id's.
No, there is no proper way to ever duplicate the use of id's. Browser behavior for this is a mixed bag and cannot be relied on.
Use classes instead or create dynamic id's for each form.
Not really, no.
When you use IDs, you're telling the browser that the ID is uniquely available, on the page.
If you have two forms with the same IDs, that means you've broken that guarantee.
An example of where this causes problems is document.getElementById( ).
It will grab the first element it finds.
If there are two, it will only get you the first one.
Another issue:
forms with IDs have a habit of being saved to Window, using that ID (this behaviour isn't 100% standard across every browser with more than 2% usage, globally, today, but it's still pretty common).
Ugly solutions I've seen in this space are things like "my-id-1" "my-id-2", et cetera, where you then access them like: document.getElementById("my-id-" + x);
...really not pretty at all...
...alternatively, you can keep the forms in JS, and just attach them to the page, when you want to switch back and forth.
In this way, they can share IDs, so long as they're never attached to the page at the same time.
document.querySelector("#my-form") === form1; // true
form1.parentNode.replaceChild(form2, form1);
document.querySelector("#my-form") === form2; // true
Again, this isn't going to help you with the built-in form and input namespacing, in any meaningful way, but if that's a feature you are 100% ignoring (not a bad thing to do), this will solve those problems.
Alternatively, you could just move to classes and data-attributes (or just attributes), and life would get easier still...
If you insist on using two forms on one 'page' and share ids between them, you have few options to make it work and be relatively 'clean' about it. Not knowing your technology stack, I can't give a completely relevant answer. However you can do something similar to the below:
HTML
<div id="myFormDiv"></div>
JAVASCRIPT
$("#myFormDiv").load(urlToForm);
You would naturally need your forms to be in separate html files for this to work as there is no way you can reliably use ids on multiple elements in one html file.

Prefer id attribute or data attribute in page with many elements?

I'm creating a HTML page with many, many "widgets" (can be in the magnitude of 1000, many not visible by display:none which get's modified dynamically by user interaction).
Each widget must carry some information regarding it.
At the moment I'm building that DOM piece by piece and add a jQuery .data() to each widget. But this building of little blocks is too slow (lots of time spent in Parse HTML), so I want to switch to a new approach where I first build a string of all widgets and then add that to the DOM with innerHTML at one go. But this doesn't allow me to use the .data() during the build anymore.
As all widgets have a unique ID anyway (currently only stored in the .data()...) I could think of two obvious solutions:
Give every widget an id attribute with the widget ID as content
Give every widget an data-widgetid attribute with the ID as content
(Parallel to that I'd have a big JavaScript hash that links the widget-id to the per widget data.)
What approach should I prefer?
Performance and resources is an issue (also on mobile devices).
By the id attribute I'm interfering with the DOM, so I don't know if that could have any negative effects. (E.g. it could be that browsers are starting to build an extra look up structure for that internally that I could avoid otherwise. I'm not a browser developer, so I don't know if that is the case though)
Use ID. If you want to protect the ID namespace of the DOM, use a prefix. For example, , where "123" is the unique ID. If your application is driven by a database, the primary key conveniently provides non-colliding ID values. Otherwise use a client-side counter to generate unique IDs.
Accessing the element by ID is much faster via document.getElementById. Modern browsers allow this kind of access really fast. The internal storage is also more efficient comparing to a custom attribute. To access a custom attribute, you'll have to traverse all the matching nodes and compare them with the values of the custom attribute. Some old IE also don't support custom attributes.
If you don't need to get those elements by their id, and you will only use it to associate data with the elements, I would recommend not using id.
Since ids are unique, browsers build a hash table to retrieve elements by id so fast.
Therefore, adding ids to lots of elements that don't need them will increase the size of the hash table unnecessarily.
In average, a search in a hash table is O(1), so the size doesn't matter. However, on the worst case, it's O(n), so better have a small table if you can.
This way, if you use document.getElementById to get other elements, it may be faster.

To what extent should I enforce a DOM element's ID's uniqueness?

I'm experiencing an issue which has two possible solutions. I am unsure which solution is 'more correct.'
I have a modal dialog which opens above a page. This dialog hosts an element with ID "foo".
Now, that is not an issue at all on its own. However, the non-dialog page has multiple tabs. One of these other tabs, deep down in some content, also has an ID "foo." This causes weird, intermittent issues depending on which "foo" is encountered first when traversing the DOM tree.
Unfortunately, both values are presenting exactly the same information in exactly the same way. The only difference is that one is being hosted inside of a dialog. The same model is being used -- thus the same ID is being generated by my ViewModel.
I can do one of two things:
Explicitly override one of these ID-creation methods to ensure that the ID generated in the dialog is unique when compared to that of the non-dialog.
Modify my jQuery search selector to only read from the dialog's form inward -- ensuring that the dialog will not capture unexpected DOM elements.
It would seem like the second idea is a better practice. I traverse less DOM nodes and don't have to hack anything in. However, to an unknowing developer, one could still accidentally re-introduce this issue by not starting searching from the proper area.
The same issue could be said to any developer working on our application, though. They would need to know explicitly to override the ID-creation of the ViewModel such that it does not create the same ID.
Is it ever acceptable to have repeating IDs if they're encapsulated in different forms? Where does the expectation of an ID being unique cease to make sense?
The ID is generated through MVC's EditorFor HTML Helper. I can override this in such a way that it generates a form-specific ID, but that was clearly not MVC's intention.
<%= Html.EditorFor(model => model.CustomerDisplayName) %>
UPDATE: It appears I should be enforcing the IDs uniqueness. However, ASP.NET MVC3 does not seem to provide an easy way of ensuring an ID is unique to the DOM. I can append on the form's ID, but that would generate huge IDs.. not sure if that is the best call. Any thoughts?
Is it ever acceptable to have repeating IDs if they're encapsulated in
different forms?
It is never acceptable, an ID should be unique. Otherwise your document is not valid.
Where does the expectation of an ID being unique cease to make sense?
The expectation of an ID being unique never ceases to make sense. It's part of the specs to have an unique ID per element.
Do you have control over the IDs? If you do, perhaps ID isn't the most "proper" way to go about it. If you used a class instead, then:
$("#DialogID .foo")
would select it without the potential for foo being a duplicated ID. The above selector will restrict the search to within the specified dialog.
To your question "To what extent should I enforce a DOM element's ID's uniqueness?", I boldly answer "You should NOT enforce unique IDs".
"Blasphemy!", I hear the Pharisees shout, vigorously waving the W3C bible.
But I live in the real world.
1. It is impossible.
It is virtually impossible to create a large website that guarantees the uniqueness of IDs.
Modern website are assembled from unrelated components, be it plug-ins, ads, widgets, you name it. All using their own set of IDs without knowing the existence of the other IDs on the page.
Technically the unique IDs can only be enforced by using some sort of GUID.
Of course, you could qualify, prefix or suffix your IDs with a classname or parent id and turn it into a long hierarchical composed ID. In fact this isn't that bad of a practice.
But there will be no guarantee. Bottom line.
2. It's not your fault.
Repeat after me: "It's not my fault...it's not my fault...".
I find it intellectually lazy and frustrating that the W3C team keeps this all too simplistic dogma alive, without looking at the real world. Do they suggest how to enforce this rule? Of course not - it's not their job. They only write the specs. But, if no one abides the law, either all of us are evil and doomed...or the law itself is at fault.
So, once again, repeat after me: "It's not my fault...it's not my fault...".
3. A bit of creativity, perhaps?
Is there really no way to adjust that rigid rule and come up with a practical solution? Either in the specs or implemented in the browsers? A form of "id scope"? A namespace?
4. Darwin says "To survive, you need to adapt."
The only way to survive in the real world is to adapt to the real world, with all it's imperfections. Some survival advice (as suggested by others already):
The more unique you make your IDs, the less chance for errors.
When searching for IDs, assume that there may be duplicates on your page. So, limit your search as much as possible.
Now stone me, I dare you.
You should enforce ID uniqueness 100%
The DOM requires that IDs be unique within it, else it is not valid, and as you've discovered you get strange and sometimes intermittent errors if you violate that requirement.
You can introduce some way to change the ID based on context, as you've mentioned, or you could take the approach of combining classes so you have, for example <div class="main foo"> and <div class="dialog foo"> where you can then use selectors to find "div.main.foo" or "div.dialog.foo" -- or get both by using just "div.foo"

using hidden elements in pages to store data

I need to store data in html documents that are associated with elements, and that I can get to with javascript. I think I want to avoid arbitrary attributes on elements, since after reading various posts here I don't trust them. I can't use id or class, since I those are used for other things and I don't want to mess with them (my project has to work on wide varieties of html so I can't make any assumptions as to the design of the class and id structure). Another thing I need is for whatever I do, it needs to survive round-tripping through innerHTML (of a parent or ancestor element of the element I need to tag with data), with data intact.
I have considered various hidden elements (including script tags and html comments), which I insert into the document right before the element I need to tag with data. Currently my favorite is hidden form elements (i.e. input with type "hidden"). I can stick any data I want into the "value" of the element, and I can find all such elements easily enough with getElementsByTagName(). Also importantly, it doesn't seem to affect the layout of the page.
I'm curious about the ramifications of this, and if anyone can think of any any problems with it. I need to be able to put them anywhere in body of the page, before any element. For instance I might need them associated with an option element (a child of a select element), so that rules out using a hidden div, since putting one of those inside a select element is illegal. I don't think they will affect form behavior, since they don't have a name attribute (I can avoid using id's if that helps).
Any thoughts on this, or suggestions for other ways of accomplishing the same that meets my needs?
If this is going to be data for JavaScript's purposes, why even try to put it in the HTML at all? Here's a simple example of what I mean
<script type="text/javascript">
var nodeData = {
foo: {/* Data to associate with div#foo */}
, bar: {/* Data to associate with div#bar */}
};
</script>
<div id="foo">Foo!</div>
<div id="bar">Bar!</div>
It's simple and elegant since the principle of IDs being unique per document nicely matches with javascript's dictionaries requiring unique keys per entry.

Categories