HTML/JS good practices: Is it good to add too many ids? - javascript

I have an HTML page where I would like to add elements to a specific list, like so:
<div id="list-of-divs">
<div id="name-specific-id">
// content
</div>
<div id="another-id">
//content
</div>
.
.
.
<div id="yet-another-id">
</div>
Now I want to add new divs to that list, say with the following:
<div id="new-option-panel">
<input id="first-textbox-id" type="text">
<input id="second-textbox-id" type=text">
<button>Add new option</button>
Of course, the addition is done with jQuery code.
My question is: is it a good practice to add many such ids in HTML code and depend on them in my jQuery code, or is it a bad practice for both HTML and jQuery, and I should find other ways in my jQuery code (depending on DOM traversing, for example)?
Just for example of what I mean: will adding to many ids slow Javascript execution?

Id's are faster to query but very tedious to maintain, so I'd take the "performance hit" and make my code more obvious by using classes and id's only when necessary. In your example I would probably keep the id on the div but the input elements don't need one, a simple class will do for both jQuery and CSS or even no class at all.

Id selectors followed by Tag selectors are the best ones because they map directly to the native .getElementById() and .getElementByTag() methods
I would suggest that you always descend from the closest parent Id when you traverse the DOM. As in most languages there is always a readability/performance tradeoff that you will have to consider for your particular application.
Check out these performance guidelines for further insights
http://www.artzstudio.com/2009/04/jquery-performance-rules/

Related

Best practice in finding a DOM element

I want to toggle(hide/show) an element when a button is being pressed. I have two ways as to implement this:
Find the element according to its class name, e.g $('.my-content')
Find the element according to its relevant DOM position towards the button, e.g. $('#my-button').parent().next().next().next()
However, none of the above seems to me very reliable since in case someone changes the HTML code, the above approaches should not work. Is there something more reliable I am missing?
If it's a specific element, supply it with an Id value and use that
to find it.
If it's a TYPE of element, use a class name.
Other than that, there's no real conventions. Just try and make sure that somebody reading your code understands what is going on.
A very good practice is to decouple HTML, CSS and JS.
When binding javascript to DOM elements you should use javascript selectors.
Basically classes with some custom prefix (like js-) which will be used only for javascript purposes (not css style).
So whenever the DOM tree structure or the CSS class names are changed, you can still have your working JS selector
HTML
<div class="my-content js-toggle-element"></div>
JS
$('.js-toggle-element')
CSS
.my-content{ ... }
Plus, using Javascript Selectors:
makes HTML highly readable: you can easily find out what will happen to that element with that js class
allows you to easily apply/disapply that behaviour also to other elements in the future, simply by adding/removing that class in your HTML and without affecting CSS at all
<div class="my-content js-toggle-element"></div>
...
<div class="another-content-to-toggle js-toggle-element"></div>
Using jQuery will be much easiest way. Like this -
$( ".target" ).toggle();
The matched elements will be revealed or hidden immediately, with no animation, by changing the CSS display property. If the element is initially displayed, it will be hidden; if hidden, it will be shown.
Reference - jQuery Toggle
If the class or the position of the element in DOM is changing then you can try
selecting it with the inner text
$("button:contains('buttontextgoeshere')")

How to select element in Protractor by html attribute when value contains

Struggling with how to select an element that doesn't have a standard unique id or class. How would I select this input element with Protractor?
Note: I cannot use the class ComboBoxInput_Default class, as this drop down box is used on several other page elements. There also isn't any easily identifiable parent element for at least 10+ DOM levels.
<div style="display:inline; white-space: nowrap;" id="ctl00_ctl31_g_b56afa08_7869_450c_8871_f6759a89d9b1_ctl00_WPQ3txtFields_ddPositioList_10_Solution_MultiComboSelection" class="ComboBox_Default">
<input type="text" style="width: 133px; height: 15px;" delimiter=";" class="ComboBoxInput_Default" value="-select-" name="ctl00$ctl31$g_b56afa08_7869_450c_8871_f6759a89d9b1$ctl00$WPQ3txtFields_ddPositioList_10_Solution_MultiComboSelection_Input" id="ctl00_ctl31_g_b56afa08_7869_450c_8871_f6759a89d9b1_ctl00_WPQ3txtFields_ddPositioList_10_Solution_MultiComboSelection_Input" autocomplete="off">
<div>
The only identifying markup that makes each of these inputs different is appended to the end of the generated id, Solution_MultiComboSelection_Input.
If I had to get this element with jquery, I would use the (not preferrable) contains $( "input[name*='Solution_MultiComboSelection_Input']" ). Is there some comparable way to locate elements in this way with Protractor?
Sure, use the "contains" or "ends-with" CSS selector:
element(by.css("input[id*=Solution_MultiComboSelection_Input]"));
element(by.css("input[id$=Solution_MultiComboSelection_Input]"));
If that is the only element with ComboBoxInput_Default as the class, then you could select it using
element(by.css('.ComboBoxInput_Default'))
This page has a lot of examples for selectors.
Hard to know for sure without seeing the rest of the page but I'd try...
$('input[value="-select-"].ComboBoxInput_Default);
or maybe
$('div.ComboBoxInput_Default input.ComboBoxInput_Default);
That said, the best solution is to have an identifier added to the code. Hope this helps!

HTML: onload for non-body elements

My normal way of performing javascript transforms on lots of elements in via JQuery:
<div class="cow"></div>
<script> $(".cow").doStuff() </script>
However, this operation is fragile and brittle: it works on the assumption that the page is all loaded only once. Once you start getting into Ajax and partial reloads, this sort of global transform breaks down completely. It also doesn't work if the server wants to do a different transform to each element based on some serverside data.
I know the actual onload event for non-body elements doesn't work. One solution is to give all the elements IDs/classes and reference them immediately using JQuery:
<div id="cow"></div>
<script> $("#cow").doStuff() </script>
However, that is really messy; I do not like it at all, partly because every element I give an ID pollutes the global I namespace. I am currently giving the element an effectively non-collidable ID
<div id="id877gN0icYtxi"></div>
<script> $("#id877gN0icYtxi").doStuff() </script>
based on random base64 numbers. However, this all seems like a giant hack: I can give the elements onclicks and onmousedowns and such very simply using their respective attributes, which then call a function to transform the given this.
Now, I know onload doesn't work. However, is there any way to simulate its functionality? Specifically, I want to be able to run javascript code referencing a particular tag using this without having to assign the tag an ID to them.
EDIT: Essentially I want something like
<input onclick="alert('moo')"/>
but for oncreate; my current use case is to fill an input or textarea with text, and I am currently doing:
<input id="id877gN0icYtxi"/>
<script>$("#id877gN0icYtxi").val(...)</script>
where the ... is different for every input box, and thus cannot be done easily using a "global" jquery transform. On the other hand, I cannot just set the value or attribute of the input when i create it, as it may be a textarea, and i wouldn't know. I want to do something like:
<input onload="$(this).val(...)"/>
Which doesn't work, but I wish it did. Again, the ... is set by the server and different for every input tag on the page.
EDIT2:
I'm well aware of hour JQuery is typically used to do whole-document transforms on lots of elements in the same way. The issue is that in this case, each element is being transformed in a way specific to that element, dictated by the server. The specific use case is each field has its contents pre-filled in by $().val(), and naturally every field will be filled with different contents. Giving each element a unique ID and using JQuery to do a global search to find that element again seems like an incredibly roundabout way of doing things, and of course breaks when you start Ajaxing parts of your page in and out.
EDIT3:
In short, i want to be able to write
<div onload="javascriptFunction(this)"/>
and have javascriptFunction() be run on when the <div> is created (whether it is on the initial page or inserted via ajax) and be passed the <div> as a parameter. Just as onclick would run javascriptFunction() with <div> as a parameter when the div is clicked, I want the same thing to happen, but when the <div> is created/inserted into the DOM.
I'm willing to accept any amount of setup scripts in the <head> in order to make this happen. For reasons mentioned above, I am not willing to add any <script> tags after the <div>, or to add a class or id to the <div>. Given these limitations, what's the best that can be done to simulate an onload attribute for non-body elements?
There's no such onload event in DOM spec, however DOM Level 2 spec provides Mutation event types, to allow notification of any changes to the structure of a document, including attr and text modifications, currently only modern browsers support such events and it is buggy in Internet Explorer 9!
However you could use DOMNodeInserted event to monitor the document for any change:
$(document).live("DOMNodeInserted", function(e){
$(e.target).val(...);
});
You should be careful using Mutation events, at least try to be updated! According to W3C:
Mutation event types has not yet been completely and interoperably
implemented across user agents, A new specification is under development with the aim of addressing the use cases that mutation events solves, but in more performant manner.
I guess if you google the matter, you might find some cross browser/jquery plugin for this, just in case, these links migh help:
https://developer.mozilla.org/en/DOM/Mutation_events
http://help.dottoro.com/ljfvvdnm.php#additionalEvents
http://help.dottoro.com/ljmcxjla.php
http://help.dottoro.com/ljimhdto.php
http://www.bennadel.com/blog/1623-Ask-Ben-Detecting-When-DOM-Elements-Have-Been-Removed-With-jQuery.htm
All this depends on what sort of tags you want to work with.
One thing to know is that jQuery lets you select a lot of items at once, so when you do something like $('p'), that object refers to all the p nodes.
Doing something like $('p').hide() hides all the p nodes.
jQuery selectors are (at least) as powerful as CSS selectors, so you can do some pretty semantic things in single lines.
Imagine if you had something like a list of reply boxes for a comments section or something:
-----------------------------------------
Comment #1
blah blah blah blah blah
[ Text field ] (Reply)
-----------------------------------------
Comment #2
nyark nyark nyark nyark
[ Text field ] (Reply)
-----------------------------------------
Comment #3
ubadabuba
[ Text field ] (Reply)
-----------------------------------------
Comment #4
eeeeeeeeeeeeeeeeeeeeeeeeee?
[ Text field ] (Reply)
So your DOM layout might look something like
<div class="comment" >
<h1> Comment #1 </h1>
<p> blah blah blah blah blah </p>
<input /> <button >Reply </button>
</div>
<div class="comment" >
<h1> Comment #2 </h1>
<p> nyark nyark nyark nyark </p>
<input /> <button >Reply </button>
</div>
so if you want to update all your input fields, to put in a default text, you just need to see that the CSS selector for your fields is .comment > input.
After that the JS appears by itself
$(document).ready(function(){
var inputs=$('.comment > input ');//references all the input fields defined by this selector
inputs.attr('value','default text');//set the value of the text in the input
})​
Example shown here
Giving elements an ID doesn't "pollute the global namespace", it's just a mechanism for referencing elements. You only need an ID for an element that you intend referencing by ID. Giving IDs to other elements isn't harmful, just unnecessary.
You can reference elements by a number of criteria, including class and their position in the DOM (any method in the CSS selectors pool and also DOM relationships—parent, child, sibling, etc.). The method you chose may have advantages and disadvantages depending on how you are using it, there is no inherently "good" or "best" way to do it, just that some methods will suit some cases better than others.
If you want to replace, say, listeners after replacing elements in the DOM, then your for replacing the elements needs to account for that. Event delegation is one strategy, there are others.
What about window.onload() ?

HTML Classes WITH IDs

I'm a little confused about HTML classes and IDs, as I'd like to use them BOTH to describe an HTML element. Is this valid and supported by most browsers?
The motivation for me to use both is this:
I have a CSS style that I would like applied to multiple elements.
I have some AJAX and Javascript that will manipulate those same elements, so I need a way to identify which element is which using an ID.
So I'd like to use an id to identify each element for JS manipulation AND at the same time I would like to specify a class so that the same style is applied from the same css.
An ID would be unique to only one item, where a class can be used to group many items together. You can use them together as you stated, ID as a unique identifier for Javascript and the class to markup with CSS.
Search for html class vs id to get many articles about this topic.
Example:
<ul>
<li class="odd" id="item1">First Item in the List</li>
<li class="even" id="item2">Second Item in the List</li>
<li class="odd" id="item3">Third Item in the List</li>
</ul>
Yes, it is perfectly valid to use both the ID and Class properties in one element. Example:
<div class="infoBox" id="myUniqueStyle"> *content* </div>
Still, keep in mind that an ID can only be used once (hence its name), while you can use classes as many times as you'd like througout a document. You can still use both the ID and the class to apply styles, while only the ID is a simple way of reaching the element through javascript.
A good way of doing it is applying IDs to all elements that you know are unique (header, navigation, main containers etc.), and classes to everything else.
"Is the" applies to elements using ID: "This is the navigation bar", "this is the header"
"Is a" or "is an" applies to elements using classes: "This is a blogPost", "this is an infoBox" etc.
You can definitely use both if you need to.
An ID is typically used to identify structural sections of your site - you should have only one element with a particular ID and any element can have only one ID.
A class is used to set styles which might be used in more than one place in your HTML file - any element can have multiple classes set.
A typical HTML document using both IDs and classes might be something like
<html>
...
<body>
<div id="header"></div>
<ul id="nav" class="full-width dark">...</ul>
<div id="content">
<div id="important-container" class="class-set-by-javascript another-class-set-by-javascript"></div>
</div>
</body>
</html>
Yes, any normal browser should allow the setting of CSS classes regardless of element id. However, setting styles on a specific element (using ids, for example) may override styles set through a CSS class.
Just a very obscure note about combining class and id in your CSS declarations, there's a bug with IE6, if you have:
two or more pages which have an
element with the same id
those elements have different
classes
you're styling them using an
#idname.classname rule
then only the first rule in the stylesheet will take effect.
See this page for details
Yes it is valid in all browsers. ID expresses just the unique IDentification of your html control through others, and class applies some style to it. Use IDs when there is only one occurence per page. Use classes when there are one or more occurences per page.

How to implement unobtrusive javascript with dynamic content generation?

I write a lot of dynamically generated content (developing under PHP) and I use jQuery to add extra flexibility and functionality to my projects.
Thing is that it's rather hard to add JavaScript in an unobtrusive manner. Here's an example:
You have to generate a random number of div elements each with different functionality triggered onClick. I can use the onclick attribute on my div elements to call a JS function with a parameter but that is just a bad solution. Also I could generate some jQuery code along with each div in my PHP for loop, but then again this won't be entirely unobtrusive.
So what's the solution in situations like this?
You need to add something to the divs that defines what type of behaviour they have, then use jQuery to select those divs and add the behaviour. One option is to use the class attribute, although arguably this should be used for presentation rather than behaviour. An alternative would be the rel attribute, but I usually find that you also want to specify different CSS for each behaviour, so class is probably ok in this instance.
So for instance, lets assume you want odd and even behaviour:
<div class="odd">...</div>
<div class="even">...</div>
<div class="odd">...</div>
<div class="even">...</div>
Then in jQuery:
$(document).load(function() {
$('.odd').click(function(el) {
// do stuff
});
$('.even').click(function(el) {
// dostuff
});
});
jQuery has a very powerful selector engine that can find based on any CSS based selector, and also support some XPath and its own selectors. Get to know them! http://docs.jquery.com/Selectors
I would recommend that you use this thing called "Event delegation". This is how it works.
So, if you want to update an area, say a div, and you want to handle events unobtrusively, you attach an event handler to the div itself. Use any framework you prefer to do this. The event attachment can happen at any time, regardless of if you've updated the div or not.
The event handler attached to this div will receive the event object as one of it's arguments. Using this event object, you can then figure which element triggered the event. You could update the div any number of times: events generated by the children of the div will bubble up to the div where you can catch and handle them.
This also turns out to be a huge performance optimization if you are thinking about attaching multiple handlers to many elements inside the div.
I would recommend disregarding the W3C standards and writing out HTML-properties on the elements that need handlers attached to them:
Note: this will not break the rendering of the page!
<ul>
<li handler="doAlertOne"></li>
<li handler="doAlertTwo"></li>
<li handler="doAlertThree"></li>
</ul>
Declare a few functions:
function doAlertOne() { }
function doAlertTwo() { }
function doAlertThree() { }
And then using jQuery like so:
$("ul li").each(function ()
{
switch($(this).attr("handler"))
{
case "doAlertOne":
doAlertOne();
break;
case ... etc.
}
});
Be pragmatic.
It's a bit hard to tell from your question, but perhaps you can use different jQuery selectors to set up different click behaviours? For example, say you have the following:
<div class="section-1">
<div></div>
</div>
<div class="section-2">
<div></div>
</div>
Perhaps you could do the following in jQuery:
$('.section-1 div').onclick(...one set of functionality...);
$('.section-2 div').onclick(...another set of functionality...);
Basically, decide based on context what needs to happen. You could also select all of the divs and test for some parent or child element to determine what functionality they get.
I'd have to know more about the specifics of your situation to give more focused advice, but maybe this will get you started.
I haven't don't really know about JQuery, but I do know that the DOJO toolkit does make highly unobtrusive Javascript possible.
Take a look at the example here: http://dojocampus.org/explorer/#Dojo_Query_Adding%20Events
The demo dynamically adds events to a purely html table based on classes.
Another example is the behaviour features, described here:http://dojocampus.org/content/2008/03/26/cleaning-your-markup-with-dojobehavior/

Categories

Resources