Javascript: getElementById vs getElementsById (both works on different pages) - javascript

I'm struggling with a really weird problem...
I have two pages (quite the sames) where I need to disable some selects. On one of them (say page A), I use getElementById to retrieve my element, and on the second one (say page B) I use getElementsById (with a 's') to retrieve it (and it works on both cases).
What is weird is that if I use getElementsById on page A (with the 's'), it gives me the error "document.getElementsById is not a function", which is normal because this function (with the 's') normally doesn't exist. But I don't have this error on page B, and if I use getElementById (without the 's') on this page, it doesn't works !?!?
Can someone give me an explanation ? (I'll lose the few hairs left on my head if it continue ...)
Thanks in advance!
Ps: Sorry for my poor english!
Edit: Here is the code of my pages:
Page A:
function controleDelaiFranchise (casChoix){
var estAvecGarantie = <bean:write property="avecGarantie" name="simulationAutonomeForm" filter="false"/>;
if(estAvecGarantie ==true){
if(casChoix == 'Emprunteur'){
document.getElementById("assDelaiFranchiseEmpr").disabled = false;
}
else {
if(casChoix == 'CoEmprunteur'){
document.getElementById("assDelaiFranchiseCoEmpr").disabled = false;
}
}
}
else{
if(casChoix == 'Emprunteur'){
document.getElementsById("assDelaiFranchiseEmpr").disabled = true;
}
else {
if(casChoix == 'CoEmprunteur'){
document.getElementById("assDelaiFranchiseCoEmpr").disabled = true;
}
}
}
Page B:
function controleDelaiFranchise (casChoix){
var estAvecGarantie = document.getElementsByName("estAvecGarantie")[0].value;
if(estAvecGarantie){
if(casChoix == 'Emprunteur'){
document.getElementsById("assDelaiFranchiseEmpr").disabled = false;
}
else {
if(casChoix == 'CoEmprunteur'){
document.getElementsById("assDelaiFranchiseCoEmpr").disabled = false;
}
}
} else {
if(casChoix == 'Emprunteur'){
document.getElementsById("assDelaiFranchiseEmpr").disabled = true;
}
else {
if(casChoix == 'CoEmprunteur'){
document.getElementsById("assDelaiFranchiseCoEmpr").disabled = true;
}
}
}
}
Edit 2:
Ok so when it was not working on page B (without 's') I had
var estAvecGarantie = document.getElementsByName("estAvecGarantie")[0].value;
if(estAvecGarantie){ ... }
I replace it with
var estAvecGarantie = document.getElementsByName("estAvecGarantie")[0].value;
if(estAvecGarantie == true) { ... }
and now it works using getElementById without the 's'
But I still don't understand why it's still working with this damn 's' ... So my problem is solved (ish), but still, if someone have an explanation for why can I used getElementsbyId() even if the function doesn't exist (and specifically on one page only), I'm all ears because I hate when I don't understand ...

As described by James here id values have to be unique in a document, so there will be only one "element" that matches, rather than multiple "elements".
That is the reason, We should not use s while selecting elements. As Id can be selected only one at a time.
However, there are methods that return multiple elements which do use the plural "elements", such as getElementsByTagName.
Hope that clears your confusion

First things first:
Function-, or rather, methodnames in JavaScript are Case-Sensitive. This means that document.getElementById is not the same as document.getElementbyId.
The weird part:
document.getElementsById does not exsist in JavaScript, so it can't work by default. The only way this can work is if somebody created this function/method on the other page. A more obvious explanation is that you made a type-o on your second page. Maybe you forgot to write the S and you thought you didn't. Can you try again?

Related

How to compare if an HTML element exists in the node array?

selectedContentWrap: HTML nodes.
htmlVarTag: is an string.
How do I check if the HTML element exists in the nodes?
The htmlVarTag is a string and don't understand how to convert it so it check again if there is a tag like that so that if there is I can remove it?
here is output of my nodes that is stored in selectedContentWrap
var checkingElement = $scope.checkIfHTMLinside(selectedContentWrap,htmlVarTag );
$scope.checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
var node = htmlVarTag.parentNode;
while (node != null) {
if (node == selectedContentWrap) {
return true;
}
node = node.parentNode;
}
return false;
}
Well if you could paste the content of selectedContentWrap I would be able to test this code, but I think this would work
// Code goes here
var checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
for (item of selectedContentWrap) {
if (item.nodeName.toLowerCase() == htmlVarTag.toLowerCase()){
return true;
}
}
return false;
}
Simplest is use angular.element which is a subset of jQuery compatible methods
$scope.checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
// use filter() on array and return filtered array length as boolean
return selectedContentWrap.filter(function(str){
// return length of tag collection found as boolean
return angular.element('<div>').append(str).find(htmlVarTag).length
}).length;
});
Still not 100% clear if objective is only to look for a specific tag or any tags (ie differentiate from text only)
Or as casually mentioned to actually remove the tag
If you want to remove the tag it's not clear if you simply want to unwrap it or remove it's content also ... both easily achieved using angular.element
Try using: node.innerHTML and checking against that
is it me or post a question on stackoverflow and 20min after test testing I figure it.,...
the answer is that in the selectedContentWrap I already got list of nodes, all I need to do i compare , so a simple if for loop will fit.
To compare the names I just need to use .nodeName as that works cross browser ( correct me if I am wrong)
Some dev say that "dictionary of tag names and anonymous closures instead" - but couldn't find anything. If anyone has this library could you please post it to the question?
here is my code.
var node = selectedContentWrap;
console.log('node that is selectedwrapper', selectedContentWrap)
for (var i = 0; i < selectedContentWrap.length; i++) {
console.log('tag name is ',selectedContentWrap[i].nodeName);
var temptagname = selectedContentWrap[i].nodeName; // for debugging
if(selectedContentWrap[i].nodeName == 'B' ){
console.log('contains element B');
}
}

Find first <a> tag whose href matches regex

I'm building a chrome extension, and one thing this extension does is to look for the first <a> tag in the current page whose href attribute matches a given regex. JS only.
I have a several solutions in mind, I tried them, but each time, the page freezes because of the solution I tried (i.e. if I comment the lines doing this logic, the pages loads correctly). So I need a fast solution.
Here is what I tried:
Solution 1: Xpath
var reg = something;
var result = document.evaluate(
'//*[local-name()="a"][contains(#href, "rss") or contains(#href, "feed")]', //first filtering
document, null, 0, null
);
var item;
while (item = result.iterateNext()) {
if (item.href.matches(reg)) // second and real filtering
return item.href;
}
Page freezes.
Solution 2: Xpath using matches()
var result = document.evaluate(
"//*[local-name()='a'][matches(#href, my_regex)]", //first filtering
document, null, 0, null
);
var item;
while (item = result.iterateNext()) {
return item.href;
}
I tried to hardcode my_regex between ''s, but I got an error in the chrome console (not a valid Xpath expression). Even putting some as simple as [matches(#href, 'rss')] gives the same error. Suspecting something related to xpath 1.0 or 2.0, but didn't investigate too long
Solution 3: document.body.innerHTML.match()
if (url = document.body.innerHTML.toString().match(reg)[0])
return url;
Page freezes.
So now I have not so many ideas left, maybe try to investigate using the xpath's match(), but that's basically all. Any thoughts from you guys?
Here's a solution that you can adapt to look for strings, regexps or both:
var string_match = "";
var regexp_match = new RegExp("www.*", "i");
var filter = {
acceptNode: function(node){
if((node.nodeType === 1) && (node.tagName === "A")){
return NodeFilter.FILTER_ACCEPT;
}
}
}
var tree_walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, filter, false);
while(tree_walker.nextNode()){
if(tree_walker.currentNode.href === string_match){
console.log(tree_walker.currentNode);
break;
}else if(regexp_match.test(tree_walker.currentNode.href)){
console.log(tree_walker.currentNode);
break;
}
}
here's the fiddle: http://jsfiddle.net/59vFt/2/
I'm using document.TreeWalker which I think is more asynchronous that getting element tags and stuff, although that will also work.
Btw, innerHTML is terrible - try to avoid using it :P

javascript: not having to use all the IDs in a html file?

I want to use the same .js for a bunch of html pages, but not necessarily all the ID's from this .js in every single page. Right now, if I don't use one ID; no ID's are showing at all.
var yes = 'Yes';
var no = 'No';
var available = 'Available: ';
document.getElementById("001").innerHTML=available+yes;
document.getElementById("002").innerHTML=available+no;
document.getElementById("003").innerHTML=available+yes;
A html with this works fine:
<div id="001"></div>
<div id="002"></div>
<div id="003"></div>
A html with this, not so fine:
<div id="002"></div>
<div id="003"></div>
What to do to make it run even though some ID's arn't being used?
Complete noob to this, there's probably a super simple solution to it(?) - but I can't find it. Hopefully, someone here can help me - without totally bashing me, or telling me how much of a bad praxis this is and that I should rewrite it in some mega haxxor language that I havn't even heard of :D
Thanks in advance!
While I'd question why you'd need incrementing numeric IDs like that, one solution would simply be to keep an map of IDs to values, then iterate the map checking for null.
var m = {
"001":yes,
"002":no,
"003":yes
};
for (var p in m) {
var el = document.getElementById(p);
if (el) // or if (el !== null)
el.innerHTML = available + m[p];
}
The document.getElementById() function returns null if no matching element is found. null.innerHTML is an error that stops the current script executing. So you just have to test for null:
var el = document.getElementById("001");
if (el != null) el.innerHTML = available + yes;
The null test can be simplified to:
if (el) el.innerHTML = available + yes;
If element "001" is always going to be "yes", "002" is always going to be "no" and so forth then you can do this:
var results = {"001" : yes, "002" : no, "003" : yes};
var el, k;
for (k in results) {
el = document.getElementById(k);
if (el) el.innerHTML = available + results[k];
}
wrap it with if(document.getElementById("id")!==null) ?
Just wrap the whole thing in a try statement to avoid any issues and put code afterwards into a finally statement::
try{
document.getElementById("001").innerHTML=available+yes;
document.getElementById("002").innerHTML=available+no;
document.getElementById("003").innerHTML=available+yes;
//etc
}
finally{
//any other code that there is after the id stuff
}
that'll prevent errors, so if something fails, it will still continue

Trying to make div background change color when 2 ids are true

I am trying to make each div's background change color when 2 ids exist. it is not changing the color. I cannot figure out what I am doing wrong. I am brand new to javascript. I have an embedded stylesheet and dont know if the javascript will override the css.
Also, I know some PHP and want to 'echo' the variables throughout the program so that I can see what the string value is in order to debug my own code. what is the easiest way to do this?
function drop(ev){
ev.preventDefault();
var image = ev.dataTransfer.getData("content");
ev.target.appendChild(document.getElementById(image));
var mydiv = '';
for (var i=0;i<9;i++)
{
if ($('#target'.i).find('#answer'.i).length == 1)
{
mydiv = document.getElementById('target'+i);
mydiv.style.backgroundColor = '#00CC00';
}
else
{
mydiv = document.getElementById('target'+i);
mydiv.style.backgroundColor = '#FF0000';
}
}
}
I think your problem may be on this line you have . not + to build the id's correctly.
if ($('#target'.i).find('#answer'.i).length == 1)
so your code should be:
if ($('#target'+i).find('#answer'+i).length == 1)
Keeping in mind I'm no jQuery wizard, my first notion was something like this:
$('div[id^=target]').each(function() {
var el = $(this).find('div[id^=answer]').addBack();
el.css('backgroundColor', el.length > 1 ? '#00CC00' : '#FF0000');
});
...but then I noticed that unlike your example, I was changing both the parent and child div. Something like this might be closer to your intent:
$('div[id^=target]').css('backgroundColor', function () {
return $(this).find('div[id^=answer]').length ? '#00CC00' : '#FF0000';
});
You also could retain the for loop if that's your preference:
for (var i = 0; i < 9; ++i) {
$('div#target' + i).css('backgroundColor', function() {
return $(this).find('div#answer' + i).length ? '#00CC00' : '#FF0000';
});
}
...and, just for fun, something kinda esoteric:
$('div[id^=target]:has(div[id^=answer])').css('backgroundColor', '#00CC00');
$('div[id^=target]:not(:has(div[id^=answer]))').css('backgroundColor', '#FF0000');
Fiddle!
Your code should work (see fiddle) with the correct operator for concatenation, i.e. with + instead of ., however here are a few points you should bear in mind :
Point 1 :
Among all the i variables you're iterating over in your for loop, if there is no div with id equal to "target" + i you will end up in the following else block :
else
{
mydiv = document.getElementById('target'+i); // null
mydiv.style.backgroundColor = '#FF0000';
}
At that place mydiv will be null and mydiv.style will throw an error.
Point 2 :
It seems you used jQuery to find the answers elements, while you used document.getElementById, which is part of the DOM API, to select then the target element. It would have been more consistent to use jQuery there too.
Point 3 :
If you want to simply output the value of some variable you can use console.log, which will output in the javascript console of the browser. The console object is provided by the browser, therefore you may not have the console.log method, but if you are using an up to date browser there is a good chance you will have it.
To summarize, see this fiddle for an example that takes these points into account.

jquery each loop only looping once and if using else code stops

I've got two problems with the following javascript and jquery code.
The jquery each loop only iterates once, it gets the first element with the right ID does what it needs to do and stops.
The second problems is that when I use the else in the code the one inside the each function, it doesn't even tries the next if, it just exits there.
I'm probably doing something fundamental wrong, but from the jquery each function and what I'd expect from an else, I don't see it.
Javascript code:
var $checkVal;
var $checkFailed;
$("#compliance").live("keypress", function (e) {
if (e.which == 10 || e.which == 13) {
var checkID = $(this).parents('td').next().attr('id');
var checkVal = $(this).val();
$('#' + checkID).each(function () {
var cellVal = $(this).text();
if (checkVal == cellVal) {
$(this).removeClass("compFail").addClass("compOk");
} else {
$(this).removeClass("compOk").addClass("compFail");
var checkFailed = True;
}
});
if (checkFailed == 'True') {
(this).addClass("compFail");
} else {
(this).addClass("compOk");
}
}
});
How could I get the each loop to iterate through all instances of each element with the id assigned to the variable checkID, and get the code to continue after the else, so it can do the last if?
An id should appear on a page only once. If you want to have multiple elements with same id, then use a class, not an id.
Your each loop iter only once because you are selecting by id thus you are selecting only one element in the page. If you change you elements to a class it should work like you expect.
This is to illustrate what I'm talking about in my comment, so that you do not remove the wrong var:
var checkVal;
var checkFailed;
$("#compliance").live("keypress", function (e) {
if (e.which == 10 || e.which == 13) {
var checkID = $(this).parents('td').next().attr('id');
//HERE is the first edit
checkVal = $(this).val();
$('#' + checkID).each(function () {
var cellVal = $(this).text();
if (checkVal == cellVal) {
$(this).removeClass("compFail").addClass("compOk");
} else {
$(this).removeClass("compOk").addClass("compFail");
//HERE is the second
checkFailed = True;
}
});
if (checkFailed == 'True') {
(this).addClass("compFail");
} else {
(this).addClass("compOk");
}
}
});
Normally, the way you have it would cause a compile-time error (in a typed language like C#) for redeclaring a variable. Here, it's not clear to me if it will be used as a local variable (ignoring your global variable) or if javascript will combine them and consider them the same. Either way, you should use it as I have shown so that your intent is more clear.
EDIT: I have removed the $ from your variables (var $checkVal) as on jsFiddle it was causing issues. SO if you do not need those $'s, then remove them. Also, note that testing on jsFiddle indicates that you do not need to change your code (other than possibly removing the $ from your declaration) as javascript appears to consider them the same variable, despite the redeclaration, which I find a bit suprising tbh.
The jquery each loop only iterates once, it gets the first element
with the right ID does what it needs to do and stops.
Yes, this is absolutely right for the code you're using:
$('#' + checkID).each(function(){};)
ID attributes are unique. There must be only one element with a given ID in the DOM. Your selector can match only one element. You are iterating over a collection containing just 1 item.

Categories