I am complete beginner in JS, so pardon my ignorance. I also didn't manage to find the answer to my question, although, undoubtedly, someone must have asked it before... so I'm posting here.
Here is a simple form I wrote:
<head>
<title>wowzy</title>
<link href="stylesheet.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<form name="forma" id="prvaForma" action="/nonsense.php" method="POST">
<label for="first">1st Value</label>
<input id="first" type="text" name="firstField" value="1st Value here" onClick = 'check();'>
<label for="second">2nd Value</label>
<input id="second" type="text" name="2ndField" value="2nd Value here">
<input type="submit" value="SUBMIT!">
</form>
</body>
and here is the JS function inside the scripts tag following the body
function check() {
var v1 = document.body.prvaForma.first.value;
alert("this is the value: " + v1);
}
Now, this doesn't work and I have been told what to do to make it work, e.g.
var v1 = document.getElementById("first").value
however, I would like to know why does it not work. I just imagined the DOM model in my head, and contructed the path that leads toward the element I want to select, i.e.
document.body.prvaForma.first.value
You can clearly see the DOM hierarchy here, and in the end, selecting the member "value" of the element.
I've been mostly programming in MATLAB and C, and I have some OO background in C++. JS seems awfully confusing right now, so any help is appreciated.
I just imagined the DOM model in my head, and constructed the path that
leads toward the element I want to select, i.e.
document.body.prvaForma.first.value
But DOM nodes do not have properties on them that match such paths! They only would collide with regular properties.
DOM nodes do have properties that allow them to access parent and child nodes (which builds up the DOM tree), and they do store some information about the node itself (tagname, attributes such as id or class).
Usually, you would use methods on them to find nodes in the tree, the most prominent of which is by using DOM selectors (you know them from CSS). Including shortcut methods like getElementById or getElementsByClassName.
And then there are some shortcut properties on the nodes, like .children which yields a collection of all element-typed child nodes. Similarly, there is a .body shortcut on the document which accesses the <body> node in the document. But these are exceptions!
The exception you might be looking for is the .forms collection, which holds all the <form> elements in a document by their name (attribute). And each form has an .elements collection, which holds inputs in that formular by their name (attribute). So you might use
document.forms["forma"].elements["firstField"]
but accessing the input by its id is much cleaner.
As you are taking your first steps in JS, I recommend using the developer console to debug and explore the language. Open in with the F12 key.
Now, being specific to your question, take a look at the documentation of document.forms, you might find it useful to select values inside forms.
This is what DOM level 0 looks like (DOM2 and 3 are almost identical) - http://www.technologyuk.net/the_internet/web/document_object_model.shtml
So to access your form items you would use
var ele = document.forms['prvaForma'].elements;
var val = ele['firstField'].value
Given the following HTML:
<fieldset>
<legend>
<input type="checkbox" name="amazon_activated" />
</legend>
<table>
<tr>
<td>
Amazon Data
</td>
</tr>
</table>
</fieldset>
next code allows hide/show the data container table related to the checkbox:
$("input[name='amazon_activated']").click(function(){
$(this).parent("legend").next("table").toggle( $(this).is(":checked") );
});
and next code it should init the hide/show state after page's load:
if ( ! $("input[name='amazon_activated']").is(":checked")){
$(this).parent("legend").next("table").hide();
}
Well, it is failing.
I already know why: this refers to the page element, not the checkbox element.
So I wonder:
Is it the best policy immediately choose an id/class:
$("#id")
for the important elements in order to facilitate their control through jquery, over form-based selectors?:
$("input[name='amazon_activated']")
Cache the element.
var element = this;
When this is in the right context.
You usually code Javascript for a given markup, not the other way around. ID's and classes have meaning other than being used as selectors.
I think you can cache your elements at the top of the outer function. It will be convenient to use them in other places. Also, caching elements will also enhance the performance.
You can do something like this:
var input = $("input[type='checkbox']"),
table = $("fieldset table");
if ( ! $("input[name='amazon_activated']").is(":checked")){
$(this).parent("legend").next("table").hide();
}
within above code this belong to page element scope, where
$("input[name='amazon_activated']").click(function(){
$(this).parent("legend").next("table").toggle( $(this).is(":checked") );
});
in above code this belongs to input[name='amazon_activated']'s callback scope which point to input[name='amazon_activated'].
So to make active first code you should try
if ( ! $("input[name='amazon_activated']").is(":checked")){
$("input[name='amazon_activated']").parent("legend").next("table").hide();
}
Its better to keep reference of input in a variable and use that
var checkboxElement = $("input[name='amazon_activated']"); // you can also use id
then you this like
if ( ! checkboxElement.is(":checked")){
checkboxElement.parent("legend").next("table").hide();
}
checkboxElement.click(function(){
$(this).parent("legend").next("table").toggle( $(this).is(":checked") );
});
I think the reason every answer is telling you to cache your objects is that it will be tremendously faster. To answer the specific question, disregarding the rest, i.e.:
Is it the best policy immediately choose an id/class for the important
elements in order to facilitate their control through jquery, over
form-based selectors?
First, I would say "attribute selectors" over "form-based selectors" as I don't believe jQuery distinguishes between, say $('input[name="amazon_activated"]') and $('a[href="#"]') as far as how it searches.
That said, the general rule of thumb is:
id selectors are faster than class selectors are faster than attribute
selectors.
So, if all you care about is jQuery speed, that's key. However, adding ids and classes, only for the sake of targeting via jQuery could slow down your page load times more than the corresponding speed-up in selector performance.
In summary to this overly-long answer:
Cache the result of a jQuery selector when possible
Use ids and classes when possible, but
Don't add unnecessary ids and classes unless testing proves them necessary
Thanks to all for your answers. All have been very helpful (I give +1 to all).
I want to add my final solutions that takes in account your tips.
Indeed there are several checkbox elements involved, so I have cached them and use an associative array to iterate over it (avoiding add more id/class).
var checkboxElements = {
"amazon_activated" : $("input[name='amazon_activated']"),
"clickbank_activated" : $("input[name='clickbank_activated']"),
"ebay_activated" : $("input[name='ebay_activated']")
}
$.each(checkboxElements, function(i, el){
$(el).parent("legend").next("table").toggle( $(el).is(":checked") );
$(el).click(function(){
$(el).parent("legend").next("table").toggle( $(el).is(":checked") );
});
});
I am applying addClass and removeClass on same set of elements in mooTools.addClass works fine but removeClass takes too long approx 6-7sec.Please see the code undeneath.
$('usermailbox').getElements('input[class=emailmessages]').each(function(el){
el.getParents().addClass('unChecked');//works fine
});
$('usermailbox').getElements('input[class=emailmessages]').each(function(el){
el.getParents().removeClass('selected'); //Takes too long in IE
});
Do I have any hope ?
Right. Several issues here... Obviously, w/o seeing the DOM it is a little difficult to determine but:
you do the same operations twice. in the same instance you can addClass and removeClass
doing element.getElements("input[class=emailmessages]") vs element.getElements("input.emailmessages") is probably slower but may return inputs that have other classes as well that you may not want.
el.getParents() will return all parents. you then iterate them again. twice. are you sure you don't mean just .getParent(), singular? if so, or if its one parent only, you are applying a .each on a single element, which is an unnecessary hit.
if your logic needs to remain then consider this as a single iteration:
store and walk.
var parents = el.getParents();
parents.each(function(p) {
p.addClass("unchecked").removeClass("selected");
});
all in all:
$("usermail").getElements("input.emailmessages").each(function(el) {
var parents = el.getParents();
parents.each(function(p) {
p.addClass("unchecked").removeClass("selected");
});
});
of course, with Slick / mootools 1.3 you can do it a lot more simple:
on this dom:
<div id="usermailbox">
<div class="selected">
<input class="emailmessages" />
</div>
<div class="selected">
<input class="emailmessages" />
</div>
<div class="selected">
<input class="emailmessages" />
</div>
</div>
the following will return all the divs:
// gets parents via a reverse combinator. you can do !> div or whatever to be more specific
// document.getElements("#usermailbox input.emailmessages !") will return all parents...
// ... in a single statement, inclusive of body, html etc... same as getParents
var divs = document.id("usermailbox").getElements("input.emailmessages !> ");
// single iteration per parent:
divs.each(function(div) {
div.removeClass("selected").addClass("unChecked");
});
of course, you can just do $("useremail").getElements("div.selected,div.unChecked") to get to these divs at any time anyway.... so it all depends on your DOM and needs, there must be a reason why you are doing what you are doing.
bottom line for perf:
cache results of lookups into vars. if you call $("someid") twice, cache it in your scope. if you do $("someid").getElements() twice, that's more than twice as bad in performance... and add .getParents() twice, thats ... n-times as bad now...
avoid applying chaqined methods to collections like this: collection.addClass("foo").removeClass("bar") - it will iterate it twice or n-times, go for a single .each callback instead, it will be much faster.
try to avoid reverse combinators or parents lookups if possible, go direct. you can use nth-child etc - other ways to walk your DOM than to get to the input and walk back. Especially when you don't really need the input itself...
.getParents(selector) will limit the types of parents you want. .getParents() will return buckloads, all the way up the parent / anchor dom node, often including the same ones for siblings.
always create a local scope with anonymous functions so you don't pollute your global object or upper scopes - IE does not like that and makes accessing variables slow.
Hope some of this makes sense, at least :) good luck and remember, speed is only relative and is as good as your performance in the slowest browser you support.
Looking for the best, standards compliant, cross browser compatible, forwards compatible way to access a group of radio buttons in the DOM (this is closely related to my other most recent post...), without the use of any external libraries.
<input type="radio" value="1" name="myRadios" />One<br />
<input type="radio" value="2" name="myRadios" />Two<br />
I've read conflicting information on getElementsByName(), which seems like the proper way to do this, but I'm unclear if this is a standards compliant, forwards compatible solution. Perhaps there is a better way?
I'm not totally happy with this solution, but I did finally track this down in the Gecko DOM documentation.
name gets or sets the name attribute of an DOM object, it only applies to the following elements: anchor, applet, form, frame, iframe, image, input, map, meta, object, option, param, select and textarea.
HTMLElement.name = string;
var elName = HTMLElement.name;
var fControl = HTMLFormElement.elementName;
var controlCollection = HTMLFormElement.elements.elementName;
More here: https://developer.mozilla.org/en/DOM/element.name
So I suppose this is a standards compliant way of proceeding (though a best practice may not really exist...)
document.getElementById('myForm').elements.myRadios
I think the best thing going forwards is:
form.elements.myRadios
vs
form.myRadios
The reason being that according to the latest spec, elements.someName will return an object of RadioNodeList (if multiple elements have the same name) that contains a value attribute indicating the checked item's value.
form.myradios on the other hand will return an object of NodeList which is just an ordered collection of items, and does not have any value property.
Finding the checked items in radios and checkboxes has traditionally been a very painful exercise.
However, I am not sure how would we access all checked elements for checkboxes. Couldn't find that in the spec. Hope that is not missing.
As any seasoned JavaScript developer knows, there are many (too many) ways to do the same thing. For example, say you have a text field as follows:
<form name="myForm">
<input type="text" name="foo" id="foo" />
There are many way to access this in JavaScript:
[1] document.forms[0].elements[0];
[2] document.myForm.foo;
[3] document.getElementById('foo');
[4] document.getElementById('myForm').foo;
... and so on ...
Methods [1] and [3] are well documented in the Mozilla Gecko documentation, but neither are ideal. [1] is just too general to be useful and [3] requires both an id and a name (assuming you will be posting the data to a server side language). Ideally, it would be best to have only an id attribute or a name attribute (having both is somewhat redundant, especially if the id isn't necessary for any css, and increases the likelihood of typos, etc).
[2] seems to be the most intuitive and it seems to be widely used, but I haven't seen it referenced in the Gecko documentation and I'm worried about both forwards compatibility and cross browser compatiblity (and of course I want to be as standards compliant as possible).
So what's best practice here? Can anyone point to something in the DOM documentation or W3C specification that could resolve this?
Note I am specifically interested in a non-library solution (jQuery/Prototype).
Give your form an id only, and your input a name only:
<form id="myform">
<input type="text" name="foo">
Then the most standards-compliant and least problematic way to access your input element is via:
document.getElementById("myform").elements["foo"]
using .elements["foo"] instead of just .foo is preferable because the latter might return a property of the form named "foo" rather than a HTML element!
[1] document.forms[0].elements[0];
"No-omg-never!" comes to mind when I see this method of element access. The problem with this is that it assumes that the DOM is a normal data structure (e.g.: an array) wherein the element order is static, consistent or reliable in anyway. We know that 99.9999% of the time, that this is not the case. Reordering or input elements within the form, adding another form to the page before the form in question, or moving the form in question are all cases where this code breaks. Short story: this is very fragile. As soon as you add or move something, it's going to break.
[2] document.myForm.foo;
I'm with Sergey ILinsky on this:
Access arbitrary elements by referring to their id attribute: document.getElementById("myform");
Access named form elements by name, relative to their parent form element: document.getElementById("myform").foo;
My main issue with this method is that the name attribute is useless when applied to a form. The name is not passed to the server as part of the POST/GET and doesn't work for hash style bookmarks.
[3] document.getElementById('foo');
In my opinion, this is the most preferable method. Direct access is the most concise and clear method.
[4] document.getElementById('myForm').foo;
In my opinion, this is acceptable, but more verbose than necessary. Method #3 is preferable.
I just so happened to be watch a video from Douglas Crockford and he weighed in on this very subject. The point of interest is at -12:00. To summarize:
Document collections (document.anchor, document.form, etc) are obsolete and irrelevant (method 1).
The name attribute is used to name things, not to access them. It is for naming things like windows, input fields, and anchor tags.
"ID is the thing that you should use to uniquely identify an element so that you can get access to it. They (name and ID) used to be interchangeable, but they aren't anymore."
So there you have it. Semantically, this makes the most sense.
To access named elements placed in a form, it is a good practice to use the form object itself.
To access an arbitrary element in the DOM tree that may on occasion be found within a form, use getElementById and the element's id.
I much prefer a 5th method. That is
[5] Use the special JavaScript identifier this to pass the form or field object to the function from the event handler.
Specifically, for forms:
<form id="form1" name="form1" onsubmit="return validateForm(this)">
and
// The form validation function takes the form object as the input parameter
function validateForm(thisForm) {
if (thisform.fullname.value !=...
Using this technique, the function never has to know
- the order in which forms are defined in the page,
- the form ID, nor
- the form name
Similarly, for fields:
<input type="text" name="maxWeight">
...
<input type="text" name="item1Weight" onchange="return checkWeight(this)">
<input type="text" name="item2Weight" onchange="return checkWeight(this)">
and
function checkWeight(theField) {
if (theField.value > theField.form.maxWeight.value) {
alert ("The weight value " + theField.value + " is larger than the limit");
return false;
}
return true;
}
In this case, the function never has to know the name or id of a particular weight field, though it does need to know the name of the weight limit field.
It’s not really answering your question, but just on this part:
[3] requires both an id and a name... having both is somewhat redundant
You’ll most likely need to have an id attribute on each form field anyway, so that you can associate its <label> element with it, like this:
<label for="foo">Foo:</label>
<input type="text" name="foo" id="foo" />
This is required for accessibility (i.e. if you don’t associate form labels and controls, why do you hate blind people so much?).
It is somewhat redundant, although less so when you have checkboxes/radio buttons, where several of them can share a name. Ultimately, id and name are for different purposes, even if both are often set to the same value.
This is a bit old but I want to add a thing I think is relevant.
(I meant to comment on one or 2 threads above but it seems I need reputation 50 and I have only 21 at the time I'm writing this. :) )
Just want to say that there are times when it's much better to access the elements of a form by name rather than by id. I'm not talking about the form itself. The form, OK, you can give it an id and then access it by it. But if you have a radio button in a form, it's much easier to use it as a single object (getting and setting its value) and you can only do this by name, as far as I know.
Example:
<form id="mainForm" name="mainForm">
<input type="radio" name="R1" value="V1">choice 1<br/>
<input type="radio" name="R1" value="V2">choice 2<br/>
<input type="radio" name="R1" value="V3">choice 3
</form>
You can get/set the checked value of the radio button R1 as a whole by using
document.mainForm.R1.value
or
document.getElementById("mainForm").R1.value
So if you want to have a unitary style, you might want to always use this method, regardless of the type of form element. Me, I'm perfectly comfortable accessing radio buttons by name and text boxes by id.
Being old-fashioned I've always used the 'document.myform.myvar' syntax but I recently found it failed in Chrome (OK in Firefox and IE). It was an Ajax page (i.e. loaded into the innerHTML property of a div). Maybe Chrome didn't recognise the form as an element of the main document. I used getElementById (without referencing the form) instead and it worked OK.
OLD QUESTION, NEW INSIGHTS (2021)
After reading and learning from some of the excellent answers to this question, is needed to go back to basics and try to get more data how id and name are defined in their origins.
The result was simple and enlightening. I'm summarizing it below:
The most important fact is, by far, that id and name are so different and have so different purposes, that they are defined in separate sections of the W3/WHATWG specifications. In short:
id:
defined as part of the DOM (web document standards) specification and NOT as an HTML (markup language) thing. So, id is DOM (document), not HTML (language).
its purpose is directly related to the unique identification of a node in the document. Therefore, very suitable to access nodes.
must be unique
name:
defined as part of the HTML specifications and NOT as and DOM. Therefore, name is HTML (language), not DOM (web document).
its purposes is directly related to form submission - how a form is structured, its enctype, the data sent to the server (eg. via https), and so on.
Based on the above data and some accumulated experience as a developer, my main conclusions are:
The confusion about using id or name to access an element is caused by some main factors:
while the very term element is clearly defined in the DOM specification, it doesn't have a very clear definition on the HTML specification - which refers to DOM, xhtml namespace, etc.
we, developers, don't like to dig into the technicalities of definitions (maybe I don't need to detail this assumption here), which can cause further misunderstandings.
as a result, the term element is widely used to refer both to a HTML tag (eg, a form) and a DOM node (despite the fact that a form is a node in the DOM, but not vice-versa.)
JavaScript provides a number of ways to access things in the DOM, including the ones based on its own stuff, like (getElementsByTagName, getElementsByName) which requires some behind-the-scenes actions to retrieve the actual nodes from the DOM.
on one hand, it makes easier for the developers to access these elements; on other hand, it hides the concepts and structures that are playing important roles in that particular context.
ultimately, the above factors generate confusions that lead to necessary and clever questions like the one that originates this whole thread.
after having read the answers and comments, gathered data, burned some neurons by analyzing the data and making conclusions, my answer to the question is:
if you want to keep it strictly technical (and probably have some gain in performance), then go for id wherever is possible and makes sense - for example, it doesn't make sense to add the id everywhere; probably it doesn't make sense to use the id attribute in radio input types.
if you want to use the flexibility offered by JavaScript, but keeping the best from both worlds, I would suggest the solution provided by #Doin in this thread.
I also appreciated the Accessibility factor brought by #paul-d-waite. The same factor is explored in this answer as well.
keep in mind that not everything you can you should.
References:
HTML - name
HTML - element
DOM - id
DOM - element
HTML - Living Standard — Last Updated 5 November 2021
DOM - Living Standard — Last Updated 18 October 2021
It's also worth it to take a look at The form element
Other references in Stackoverflow:
was the name attribute really needed in html?
Difference between id and name attributes in HTML
Bonus: Short and cool distinction between id and name, for the impatient.
Just to add to everything already said, you may access the inputs either with the name or id using preferably the elements property of the Object form, because without it you may get a property of the form named "foo" rather than an HTML element. And according to #Paul D. Waite it's perfectly ok to have both name and id.
var myForm = document.getElementById("myform")
console.log(myForm.foo.value) // hey
console.log(myForm.foo2.value) // hey
//preferable
console.log(myForm.elements.foo.value) // hey
console.log(myForm.elements.foo2.value) // hey
<form id="myform">
<input type="text" name="foo" id="foo2" value="hey">
</form>
According to MDN on the HTMLFormElement.elements page
The HTMLFormElement property elements returns an
HTMLFormControlsCollection listing all the form controls contained in
the element. Independently, you can obtain just the number of
form controls using the length property.
You can access a particular form control in the returned collection by
using either an index or the element's name or id.
I prefer this one:
document.forms['idOfTheForm'].nameOfTheInputFiled.value;
The name attribute works well. It provides a reference to the elements.
parent.children - Will list all elements with a name field of the parent.
parent.elements - Will list only form elements such as input-text, text-area, etc.
var form = document.getElementById('form-1');
console.log(form.children.firstname)
console.log(form.elements.firstname)
console.log(form.elements.progressBar); // undefined
console.log(form.children.progressBar);
console.log(form.elements.submit); // undefined
<form id="form-1">
<input type="text" name="firstname" />
<input type="file" name="file" />
<progress name="progressBar" value="20" min="0" max="100" />
<textarea name="address"></textarea>
<input type="submit" name="submit" />
</form>
Note: For .elements to work, the parent needs to be a <form> tag. Whereas, .children will work on any HTML-element - such as <div>, <span>, etc.
To supplement the other answers, document.myForm.foo is the so-called DOM level 0, which is the way implemented by Netscape and thus is not really an open standard even though it is supported by most browsers.
Form 2 is ok, and form 3 is also recommended.
Redundancy between name and id is caused by the need to keep compatibility. In HTML5 some elements (such as img, form, iframe and such) will lose their "name" attribute, and it's recommended to use just their id to reference them from now on :)
Check out this page: Document.getElementsByName()
document.getElementsByName('foo')[0]; // returns you element.
It has to be 'elements' and must return an array, because more than one element could have the same name.
My answer will differ on the exact question.
If I want to access a certain element specifically, then will I use document.getElementById().
An example is computing the full name of a person, because it is building on multiple fields, but it is a repeatable formula.
If I want to access the element as part of a functional structure (a form), then will I use:
var frm = document.getElementById('frm_name');
for(var i = 0; i < frm.elements.length;i++){
..frm.elements[i]..
This is how it also works from the perspective of the business. Changes within the loop go along with functional changes in the application and are therefore meaningful. I apply it mostly for user friendly validation and preventing network calls for checking wrong data. I repeat the validation server side (and add there some more to it), but if I can help the user client side, then is that beneficial to all.
For aggregation of data (like building a pie chart based on data in the form) I use configuration documents and custom made JavaScript objects. Then is the exact meaning of the field important in relation to its context and do I use document.getElementById().
Because the case [2] document.myForm.foo is a dialect from Internet Explorer.
So instead of it, I prefer document.forms.myForm.elements.foo or document.forms["myForm"].elements["foo"].