Why element.setAttribute('checked', true) doesn't work? - javascript

I want to do something like this:
elementX.setAttribute('checked', true); // this is input type checkbox
elementY.appendChild(elementX);
It is everything ok with rendering and other things but on the page, the input is not checked.
When I look at elements in chrome console I can see:
<input type="checkbox" checked="true">
What should I do?
I've already tried
elementX.setAttribute('checked', true);
elementX.setAttribute('checked', 'true');
elementX.setAttribute('checked', 'checked');
I don't have any errors in the console.

See MDN:
checked
A Boolean attribute indicating whether or not this checkbox is checked by default (when the page loads). It does not indicate whether this checkbox is currently checked: if the checkbox’s state is changed, this content attribute does not reflect the change. (Only the HTMLInputElement’s checked IDL attribute is updated.).
While setting the checked attribute will show a change in the serialization of the element, it won't actually check the checkbox. For that, you have to invoke the setter, eg
elementX.checked = true;

Firstly, #CertainPerformance answer is the one that I upvoted, and it's correct!
I simply wanted to shed more light on the nuances of IDL( Interface Design Language ) attributes versus content attributes and what it means for them to be reflective or not.
In this case an IDL attribute is a JavaScript Property on a DOM representation of an Element. Not just any property, but one that was predefined within the W3 Specification. IDL Examples
Attributes specified in your HTML are considered content attributes. These attributes are used to populate IDL attributes on DOM Nodes during the rendering process. content attributes are accessible from the DOM Node through a few ways including the getAttribute method, but they are not stored on it as IDL attributes are. In simple terms element.getAttribute("checked") and element.checked are actually looking in two completely different objects for the key checked.
So what's it mean to be reflective?
A DOM Node's property and its HTML attribute are interchangeable from the point of rendering if the node is not altered - but also if specific attributes are changed.
Altering id and class in any way, whether directly on the DOM Node or within the Attribute object using the element.setAttribute method, will result in both values being changed.
id and class are reflective IDL attributes because they, in effect, watch their content attributes for changes and vice versa.
Alternatively checked and value IDL attributes are not reflective. When the value or checked properties are altered on either the DOM Node or the Attribute Object, it does not alter the corresponding attributes of the other.
Outside of those properties( there are more than id and class - though there's no real list of reflective vs not reflective - I would presume it's any property related to the identity of the Node in the DOM that would cause a re-render ) the content attributes and DOM Node properties are not bound together. This makes using getAttribute and setAttribute useless if the intent is to update or get current data, because the current data is only found within the DOM Node properties.
The examples below illustrate the difference:
ID change example: Both Attribute and Property Reflect Each Other
let i = document.querySelector("input");
i.addEventListener("id_change", function() {
let HTML_Attribute = i.getAttribute("id"),
DOM_Node_Property = i.id;
console.log("HTML Attribute 'value': " + HTML_Attribute +
"\n DOM Node Property 'value': " + DOM_Node_Property);
})
let n = 1;
let timer = setInterval(function() {
if(n > 2) clearInterval(timer);
i.setAttribute("id", "newId_" + String.fromCharCode(Math.floor(Math.random() * 26) + 65));
i.dispatchEvent(new CustomEvent("id_change"));
n++;
}, 1000);
<input value="Hello World"/>
Value change example: Attribute and Property Are Different
let i = document.querySelector("input");
i.addEventListener("input", function() {
let HTML_Attribute = i.getAttribute("value"),
DOM_Node_Property = i.value;
console.log("HTML Attribute 'value': " + HTML_Attribute +
"\n DOM Node Property 'value': " + DOM_Node_Property);
})
<input value="Hello World"/> <em><small>Type into the Box</small></em>

Related

jQuery data attr not setting

This appears very simple but I cannot see why it's not working. The selector is correct however the div .faqContent is simply not being updated with the data-height attribute.
$('.faqItem .faqContent').each(function(){
var h = $(this).height();
$(this).data('height',h);
});
I have checked that var h is correct, it is in colsole.log as correctly holding the height.
EDIT
It's absolutely not conflict, and console shows no errors.
The data function confuses a lot of people, it's not just you. :-)
data manages jQuery's internal data object for the element, not data-* attributes. data only uses data-* attributes to set initial values, and more, it guesses at what type you want those to be based on what they look like (so something that looks like a number is converted to a number; something that looks like JSON gets converted to an object). The data method never sets data-* attributes on elements, it only sets the data on its internal data object. That means the two (the internal data object and the attribute) get out of sync:
const t = $("#target");
let value;
// Getting the attribute always gets a string
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 1 (string)
// Using `.data`, jQuery will guess that because the attribute looks like a number,
// you want it converted to a number
value = t.data("height");
console.log(`${value} (${typeof value})`); // 1 (number)
// `data` only sets the internal data object properties, not the attribute...
t.data("height", 2);
// ...so the attribute still has `"1"`
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 1 (string)
// ...even though the data object has 2
value = t.data("height");
console.log(`${value} (${typeof value})`); // 2 (number)
<div id="target" data-height="1"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If you want to actually set a data-* attribute, use attr:
$(this).attr("data-height", h);
const t = $("#target");
let value;
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 1 (string)
// `attr` converts whatever you give it to string
t.attr("data-height", 2);
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 2 (string)
<div id="target" data-height="1"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
But if you only want this information for future use, data is fine (assuming you're okay with its automatic type conversion), just don't expect to see it in the DOM inspector, because jQuery doesn't write this information to the DOM.
You will not be able to see it in the element inspector but it is there as jquery set the data attribute internally.
try console.log($(this).data('height'));
.data() is only stores the associated new value in memory(or internally). It'll not change the attribute in the DOM hence you cannot see it updated using inspector tools.
To change the attribute, you can use .attr():
$('.faqItem .faqContent').each(function(){
var h = $(this).height();
$(this).attr('data-height',h);
});
JQuery .data() stores the value on the element itself, it won't add an attribute.
http://api.jquery.com/data/
If you want to add an attribute, use attr:
$('.faqItem .faqContent').each(function(){
var h = $(this).height();
$(this).attr('data-height', h);
});
http://api.jquery.com/attr/

Javascript: get content of an element with incorrect style definition

I'm creating a stylesheet for a certain site, using javascript to assign classes to certain elements. For whatever reason some of 'td' elements use weird class assignments and inline styles, so I loop over them, clean them up and assign proper class names to reference in external stylesheet.
I don't have access to the site itself (nor administrative permission to change anything) so I use Stylish and Greasemonkey for Firefox (plus Adblock to dispose of the original stylesheet).
Here is a little part of js code responsible for that:
var cellStyleBg = cCell.style.backgroundColor;
if (cellStyleBg) {
switch(cellStyleBg) {
case 'white':
cCell.removeAttribute('style');
if ( cCell.parentNode.nodeName == 'TR' ) {
cCell.parentNode.className = 'deadlineSet';
}
break;
...
The problem is there is one particular case where this doesn't work:
<td class="td2h" style="background: dd97f0;">
As you can see, there is no octotorp in front of the color code. I assume that is the reason in this one case the variable cellStyleBg is 'null'.
I tried (instead of 'cCell.style.backgroundColor') to use 'cCell.style' and 'cCell.style.background', but they don't return plain text for me to parse.
What can I do to handle this particular case?
I think the only way to go is to get the raw value of the style attribute.
You do this by
//will output background: dd97f0
cCell.getAttribute('style');
If needed, you can breakdown the style into key value pairs using this snippet
//give "background: dd97f0", will output "background=dd97f0"
var stylePattern = /(.*?):([^;]*);?/g
while (match = stylePattern.exec(style)) {
console.log(match[1].trim() + "=" + match[2].trim());
}

Why does renaming an HTML element remove its events?

I've noticed that when I change the ID or name of a form element via JavaScript, the jQuery events that were tied to it are no longer there. I've tried this with Firefox 17 and IE 10. Is this by design? And if so, is there any way to prevent it?
UPDATE: Please check http://jsfiddle.net/qHH7P/2/ for an example.
I'm adding the button to remove the row via jQuery. When I remove the first row, I give the elements in the second row a new name and ID. Then the remove button for that remaining row doesn't fire the event anymore. I need to rename the elements because ASP.NET MVC expects a certain naming convention for the collection of objects when binding. That's why I need to rename them with a "0" instead of "1". I'm doing my rename with
var regexpattern = new RegExp("WorkspaceQuestionSets\\[.+\\]", "g");
$(this).html($(this).html().replace(regexpattern, "WorkspaceQuestionSets[" + index + "]"));
var regexpattern = new RegExp("WorkspaceQuestionSets_.+__", "g");
$(this).html($(this).html().replace(regexpattern, "WorkspaceQuestionSets_" + index + "__"));
I just realized that I'm not even renaming the buttons. So it makes even less sense that the event is gone. But if I comment out the code to rename the elements, the event remains.
You are rewriting the HTML. .html() returns a string, which you then modify and set again. The browser will parse that HTML string and create new DOM elements from it. In this process, your destroying DOM elements and consequently loose the event handlers you bound to them before.
I just realized that I'm not even renaming the buttons. So it makes even less sense that the event is gone.
You are destroying and recreating every single element that is inside this (each row I assume), no matter whether you modified its HTML representation or not.
You have to possibilites to solve this:
Use event delegation: Instead of binding the event handlers directly to the elements, bind them to an ancestor, which will always exist. Read more about event delegation in the .on *[docs] documentation, section Direct and delegated events.
Don't rewrite the HTML. Select the elements whose name attribute you want to modify and modify it. For example:
var name_exp1 = /WorkspaceQuestionSets\[.+\]/g;
var name_exp2 = /WorkspaceQuestionSets_.+__/g;
sourceEle.closest(".table").find(".row.set").each(function(index) {
// Edit the name attribute of all `select` and `input` elements
$(this).find('select, input').prop('name', function(i, name) {
if (name_exp1.test(name)) {
return name.replace(
name_exp1,
"WorkspaceQuestionSets[" + index + "]"
);
}
else if (name_exp2.test(name)) {
return name.replace(
name_exp1,
"WorkspaceQuestionSets_" + index + "__"
);
}
return name;
});
});
In JavaScript, forget the HTML and work with the DOM.
This does not happen to me with plain JavaScript in Chrome 24.
Here's what I typed in the JS console:
document.write("<button id='bb'/>")
//undefined
var bb=document.getElementById('bb')
//undefined
bb
//<button id=​"bb">​</button>​
bb.addEventListener('click',function(){alert('hi');});
//undefined
bb
//<button id=​"bb">​</button>​
bb.id
//"bb"
bb.id="qq"
//"qq"
bb
//<button id=​"qq">​</button>​
document.getElementById("bb")
//null
document.getElementById("qq")
//<button id=​"qq">​</button>​
And importantly clicking the button made the event trigger both before and after the assignment to bb.id.
Also in Firefox 17 I didn't quite see that happening with plain JavaScript. I was on the page: http://start.ubuntu.com/12.04/Google/?sourceid=hp
And in the console here's what I issued:
[19:29:41.261] var bb=document.getElementById('sbtn')
[19:29:41.267] undefined
[19:29:45.501] bb.addEventListener('click',function(){alert('hi')});
[19:29:45.507] undefined
[19:29:48.292] bb.id
[19:29:48.298] "sbtn"
[19:29:59.613] bb.id="sbttttttn"
[19:29:59.619] "sbttttttn"
The alert still occurred after the id was changed.
This must be specific to either IE, which I cannot try, or the way jQuery sets up events or handles ID changes (perhaps invisibly destroying the object?).

What is the difference between JavaScript's getElementById() and getElementsByName() functions?

Other than the fact that my brief research tells me the latter will return a collection rather than a a single element with the ID passed.
Consider the following code:
function validateAllFields()
{
var clientid = document.getElementById("clientid");
var programs = document.getElementById("programs");
var startmonth = document.getElementById("startmonth");
var startday = document.getElementById("startday");
var startyear = document.getElementById("startyear");
var completed = document.getElementsByName("completed");
var goals = document.getElementsByName("goals");
var errors = document.getElementById("errorMsg");
errors.innerHTML = "";
if(isNumeric(clientid, errors, "Please enter a valid client ID")){
if(madeSelection(programs, errors, "Please select a program from the drop-down list")){
if(madeSelection(startmonth, errors, "Please enter a month for the start date")){
if(madeSelection(startday, errors, "Please enter a day for the start date")){
if(madeSelection(startyear, errors, "Please enter a year for the start date")){
if(checked(completed, errors, "Please choose an option that indicate whether the client has completed the program")){
if(checked(goals, errors, "Please choose an option that indicate whether the client has met his/her goals.")){
window.alert("GOT IN TO RETURN TRUE");
return true;
}
}
}
}
}
}
}
return false;
}
</script>
The above code works perfectly after placing it in the onsubmit handler of the form. However, earlier, for the elements (programs, startmonth, startday, startyear) I was using getElementsByName(), the following happened:
The code seems to get to the second line of the if blocks "if(madeSelection(programs...." and it displayed the error msg via innerHTML for a brief second and
Proceeded to submit the form AS IF the JS had indeed returned true. As you can tell, there is a popup alert right before returning true and the popup DID NOT show up at all.
Bad data was submitted to my test database because the form had not been validated. (yet to write server-side validation with this form, but I will).
please assume the elements programs, startmonth, startday, and startyear are drop-down lists with the same id and name attributes.
Also, the madeSelection function is given as:
function madeSelection(element, error, msg) {
if (element[0].value == "none" || element[0].valueOf == "none" || element[0].value == "") {
error.innerHTML = msg;
element.focus();
return false;
} else {
return true;
}
}
My code does work right now after I changed those elements to be using getElementById(), I was just wondering why getElementsByName presented such behavior.
<input type="text" name="foo" id="bar">
^^^^ ^^
getElementsByName gets elements by their name, getElementById gets the element by its id. There may be many elements on a page with the same name (hence getElementsByName always returns a list of elements), but there is (must) only be one element with a given id (therefore getElementById only returns a single element).
The GetElementsByName method returns an array, and when you tried to call element.focus() you got an error because there is no focus method on an array. When you get an error in the event handler it won't prevent the form from posting.
If you use GetElementById you should use element to access the element, and if you use GetElementsByName you should use element[0].
To expand a little on the answers already provided, the name attribute was provided early in the days of the browser DOM, to allow the contents of elements in forms to be submitted with reference to that name attribute, so that parameters could be passed to a CGI script at the server side. This dates from before the more modern ability to reference DOM elements for manipulation of such things as styles by JavaScript.
When the DOM was expanded to allow said modern manipulations, the id attribute was added, so that individual elements could be manipulated at will. When you want to perform DOM manipulations, you select elements to be manipulated either via the id attribute, if you're only interested in manipulating a single DOM element, or via the class attribute (suitably set by yourself), if you want to manipulate several elements together in the same manner. In this latter case, you can set the class attribute to multiple values (name strings separated by spaces), so that you can, for example, designate elements to belong to more than one class, and perform manipulations accordingly. You can mix and match id and class attributes practically at will, provided you exercise some care to avoid name clashes.
So, for example, you could have five buttons on your web page, all set to:
class="Set1"
and change the style of all those buttons, first by using a statement such as:
myButtons = document.getElementsByClassName("Set1");
to obtain an array of Element objects corresponding to your buttons, then running the following loop:
for (i=0; i<myButtons.length; i++)
myButtons[i].style.color="#FF0000";
to change the colour of the text to red. One of those buttons could additionally have an id attribute set to "Special", and you could then do something such as:
ref = document.getElementById("Special");
ref.style.backgroundColor = "#FFFF00";
to set the background colour of that one button in the set to yellow, to signal that it's intended for a special function within the set.
In short, use the name attribute for form submissions, and the id and class attributes for referring to elements you intend to perform DOM manipulations upon, or attach event handlers to, etc.
The name attribute is not designed to be unique, while the id attribute is.
<div name="nonUnique" />
<div id="unique" />
In order for the form to not be submitted, return false needs to be returned (you said you used the onsubmit handler)
in the second line of your code, because a selection is indeed returned by getElementsByName (it would work with .getElementsByName("test")[0] ) a js error is thrown. The rest of the code is not executed, therefore nothing is returned and the form by-passes the rest of the validation completely.
The getElementById method can access only one element at a time, and that is the element with the ID that you specified. The getElementsByName method is different. It collects an array of elements that have the name that you specified. You access the individual elements using an index which starts at 0.
getElementById
It will get only one element for you.
That element bears the ID that you specified inside the parentheses of getElementById().
getElementsByName
It will get a collection of elements whose names are all the same.
Each element is indexed with a number starting from 0 just like an array
You specify which element you wish to access by putting its index number into the square brackets in getElementsByName's syntax below.
function test() {
var str = document.getElementById("a").value;
console.log(str);
var str1 = document.getElementsByName("a")[0].value;
console.log(str1);
var str2 = document.getElementsByName("a")[1].value;
console.log(str2);
}
<input type="text" id="a" value="aValue" />
<br>
<br>
<input type="text" name="a" value="bValue" />
<br>
<br>
<input type="text" name="a" value="cValue" />
<br>
<br>
<button onclick="test()">Click Here</button>

getAttribute cannot return class in IE7?

I need to find random nodes according to random attribute values.
To do that I use getAtrribute on nodes from getElementsByTagName.
It seems like when I look for class name as attribute it does not work on IE (works on FF).
Anyone know if getAtrribute doesn't work only on 'class' or other attributes as well? (if its only class I'll do a workaround.)
It's worth testing all of your Javascript cross-platform, if you're not using something like jQuery to take the pain away, but Class may just be a special case.
This should be a cross-platform way of getting the class:
element.className
Anyone know if getAtrribute doesn't work only on 'class' or other attributes as well?
It fails for all attributes where the HTML attribute name differs from the DOM property name (className, htmlFor), plus you have to use the DOM-style capitalisation. It also returns the wrong datatype for attributes whose DOM properties aren't strings:
disabled, readOnly, checked, selected, multiple,
compact, declare, isMap, noHref, defer, noResize,
size, cols, rows, width, height, hspace, vspace,
maxLength, tabIndex, colSpan, rowSpan
and possibly others I've missed!
element.getAttribute(x)
in IE is exactly the same as saying:
element[x]
So in general you should avoid using getAttribute, and use the simple DOM Level 1/2 HTML interfaces such as ‘element.className’ instead.
This is finally fixed in IE8.
IE is broken in this respect. You can access the class in IE via getAttribute("className") but of course this isn't really the attribute so it doesn't work in !IE.
That leaves you with a choice of branching to get element.className or branching to getAttribute on "className" or "class". Not good.
You can grab a list of all attributes from your elements and test their value that way. This snippet handles both IE and WebKit browsers, and will return the string value of the CSS class:
var value = "";
var elements = document.getElementsByTagName("div");
for(var i = 0; i < elements.length; i++){
if(typeof elements[i].attributes['class'] == "undefined"){
value = elements[i].getAttribute("class");
} else {
value = elements[i].attributes['class'].nodeValue;
}
alert(value); // careful, this will be a lot of alerts
}
Here you can get and set the class attribute with cross browser compatibility.
//also works with IE7 and below
//get class attribute value
var class_name = document.getElementById('elem_id').className;
//set class attribute
document.getElementById('elem_id').className = 'new-class-name';

Categories