This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 8 years ago.
I have a table that typically looks like this on the screen:
The multiple rooms are displayed by using a foreach loop.
Now I need to disable all the second dropdown boxes when a value has been selected in one of the first ones, or vice versa.
Typical code for one of the dropdown boxes is
<select onchange="std()" class="numrooms" name="numrooms[4]">
<option value="" selected>Select</option>
<option value="1"> 1</option>
<option value="2"> 2</option>
</select>
I am using the following javascript:
function std() {
d = document.getElementsByClassNames("numrooms").value;
if (d>0) {
document.getElementsByClassNames('numrooms_nr').disabled = true;
}else{
document.getElementsByClassNames('numrooms_nr').disabled = false;
}
}
function nr() {
e = document.getElementsByClassNames("numrooms_nr").value;
if (e>0) {
document.getElementsByClassNames('numrooms').disabled = true;
}else{
document.getElementsByClassNames('numrooms').disabled = false;
}
}
but it doesn't work.
I have tried changing the classes to IDs and then using GetElementById() in the script and that does work, but of course it only works on one pair of dropdowns. I thought going to classes and using Get ElementsBy ClassName() would do the trick, but apparently not.
Am I missing something obvious? Or doing it completely wrong?
EDIT
As everyone pointed out, I wrote "getElementsByClass" in the question when it should have been "getElementsByClassName". However that was a mistake when I wrote the question and not in my actual code. I've corrected it here now.
EDIT2
I'm getting there, but not quite fully sorted yet. I've adopted #Notulysses suggestion so for testing purpose my script is
function std() {
d = document.getElementsByClassName('numrooms')[1].value;
if (d>0) {
var n = document.getElementsByClassName('numrooms_nr')
for(var i=0;i<n.length;i++){
n[i].disabled = true;
}
}else{
var n = document.getElementsByClassName('numrooms_nr')
for(var i=0;i<n.length;i++){
n[i].disabled = false;
}
}
}
function nr() {
e = document.getElementsByClassName('numrooms_nr')[0].value;
if (e>0) {
var n = document.getElementsByClassName('numrooms')
for(var i=0;i<n.length;i++){
n[i].disabled = true;
}
}else{
var n = document.getElementsByClassName('numrooms')
for(var i=0;i<n.length;i++){
n[i].disabled = false;
}
}
}
function(std) now disables all of the second dropdown boxes when the first dropdown in the second room is selected (because I have set it to 1). Similarly function(nr) disables all of the first dropdown boxes (because I have set it to [0]).
But how do I disable all the second dropdowns when any of the first dropdowns is selected?
You are using getElementsByClass (it doesn't exist) and changing property for the whole collection (not valid, you should iterate through Node list to change attribute's value). You should do something like this :
var n = document.getElementsByClassName('numrooms')
for(var i=0;i<n.length;i++){
n[i].disabled = true;
}
Its GetElementsByClassName not GetElementsByClass and it returns you NodeList of nodes so if you want to change any property you need to use indexing, i.e, looping:
document.getElementsByClassName('numrooms_nr')[0].disabled = true;
and here is your complete code:
var d = document.getElementsByClassNames("numrooms");
for(var i=d.length-1;i>=0; i--){
if(n[i].value > 0){
n[i].disabled = true;
}
else{
n[i].disabled = false;
}
}
You are using it wrong. It's not getElementsByClass , it is getElementsByClassName. And it returns a HTMLCollection of found elements. To have an access to any element you should use indexing.
document.getElementsByClassName('someclass')[0] <- index
See the link for more details -> Link
getElementsByClassName and getElementsByTagName do not return a single element, like get ElementById. Rather, they return an array containing all the elements with that class. This has tripped up many Javascripters over time. Also note that getElementsByClassName won't work in early IE versions (surprise surprise!)
As such, you are missing the bit with the [0] or [1] or the [2] etc. after getElementsByClassName is written, for example:
document.getElementsByClassName("numrooms")[0]
will refer to the first of the bunch with that class name.
Related
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');
}
}
I was checking if some elements exist with:
if ($(selector).length > 0){
....
}
However, sometimes the returned object (even if the element exists in the DOM and has returned) does not have the length attribute so this never evaluates to true.
This error appears in chrome.
Do you have any idea what the problem might be?
Edit:
I use this code:
var variable;
for(let elem in selectors){
if($(elem).length > 0){
variable = true;
break;
}
else
variable = false;
}
Given a list of selectors, variable is true if at least one of the selectors exists. This is inside a google chrome extension's content script. After this code runs in the plugin I get the same problem even in the console of google chrome.
Edit:
This code does not create a problem:
var variable;
if($(elem).length > 0){
variable = true;
}
else
variable = false;
It seams that the problem is the for loop or/and the break; statement. However, a for loop is needed to make this code work for a list of selectors and not just for one.
It seams that the document.querySelectorAll() does not create the same problem and works as expected. So I used this one instead of the $(). If you only want to check if an element exists it is working just fine.
So this code always returns true if at least one of the selectors exists in the web page:
function exists(selectors){
var selectorExists = false;
for(let i=0; i < selectors.length; i++){
let element = document.querySelectorAll(selectors[i]);
if(element.length > 0){
selectorExists = true;
break;
}
}
return selectorExists;
}
If this is because html is not loaded then we can use timeout
now what i understood from code is you are using for loop for selectors so selectors should come as array not the element within it (elem here) is not array so how elem will have length??
setTimeout(function(){
if ($(selector).length > 0){
for(let elem in selectors){
if($(elem)){
variable = true;
break;
}
else
variable = false;
}
}
},100);
I have been working on creating a custom script to help manage a secret questions form for a login page. I am trying to make all the seperate select lists dynamic, in that if a user selects a question in one, it will no longer be an option in the rest, and so on. Anyways, the problem I am having is when I try to set the variables in the other lists to null. I am currently working with only 3 lists, so I look at one list, and find/delete matches in the other 2 lists. Here is my loop for deleting any matches.
for(i=0; i<array1.length; i++) {
if(array2[i].value == txtbox1.value) {
document.questions.questions2.options[i] = null
}
if(array3[i].value == txtbox1.value) {
document.questions.questions3.options[i] = null
}
}
This works fine if both the matches are located at the same value/position in the array. But if one match is at array1[1] and the other match is at array3[7] for example, then only the first match gets deleted and not the second. Is there something I am missing? Any help is appreciated. Thanks!
I don't see too many choices here, considering that the position in each array can vary.
Do it in separate loops, unless of course you repeat values in both arrays and share the same position
EDTI I figured out a simple solution, it may work, create a function. How about a function wich recives an array as parameter.
Something like this:
function finder(var array[], var valueToFound, var question) {
for (i=0; i<array.lenght; i++) {
if (array[i].value == valueToFound) {
switch (question) {
case 1: document.questions.questions1.options[i] = null;
break;
}
return;
}
}
}
I think i make my point, perhaps it can take you in the right direction
My bet is that the code isn't getting to array3[7] because either it doesn't exist or that array2 is too short and you're getting a JavaScript exception that's stopping the code from doing the check. Is it possible that array2 and array3 are shorter than array1?
It is more code, but I would do it like this:
var selectedvalue == txtbox1.value;
for(i=0; i<array2.length; i++) { // iterate over the length of array2, not array1
if(array2[i].value == selectedvalue) {
document.questions.questions2.options[i] = null;
break; // found it, move on
}
}
for(i=0; i<array3.length; i++) {
if(array3[i].value == selectedvalue) {
document.questions.questions3.options[i] = null;
break; // you're done
}
}
This is weird, im trying to clear my select list and exactly half is being cleared, not everything. Its also every other item which is left behind. I havent included the data here, but just the setup of the select list. The data is 10 items and only 5 are deleted. UniList calls DeleteAll. The first alert prints out '10' and the second prints out '5'
DID is just document.getelementbyid()
function DeleteAll(string) {
var i;
var list = DID(string);
alert(DID(string).options.length);
for (i = 0; i<list.options.length; i++) {
list[i] = null;
}
alert(list.options.length);
alert("finished deleting");
}
<select size='12' name='UniList' id='UniList' style='width:180px;' onclick=changeuni('UniList','Uni')>
<option value=''></option>
<option value=''></option>
<option value=''></option>
</select>
list.options is a HTMLOptionsCollection that is assumed to be live:
Note: Collections in the HTML DOM are assumed to be live meaning that they are automatically updated when the underlying document is changed.
So with every iteration one item is removed and the document is updated so that list.options and thus list.options.length is updated too. So instead of removing the first, second, third, etc. option of the list you actually remove the first, third, fifth, etc. option.
Use list.options[0] instead to always remove the first option:
while (list.options.length > 0) {
list.options[0] = null;
}
Or remove the options from behind:
for (i = list.options.length-1; i>=0; i--) {
list.options[i] = null;
}
By the way: The HTMLSelectElement has a remove method to remove an option by its index:
while (list.length > 0) {
list.remove(0);
}
You could just replace your existing DeleteAll function with
function DeleteAll(listId) {
var list = document.getElementById(listId);
list.options.length=0;
}
or even
function DeleteAll(listId) {
document.getElementById(listId).options.length=0;
}
If you still prefer to use DID() you can just swap it in
This might be an option as well
while ( list.options.length > 0 )
{
list[0] = null;
}
Try looping from the end of the select list to the beginning. UI suspect as you are deleting them it is reordering the array such that once you have deleted half your next index then is over the top. eg if you have 10 items then after deleting 5 it will try to delete item with index 6 but you only have a list length of 5 so that won't work...
So basically try:
for (i = list.options.length-1 ; i>=0; i--) {
list[i] = null;
}
I think I got my indexes right there but adjust the loop accordingly in case I got it wrong.
Edit:
I realised that there is also a better way to delete. Just use list.options.length=0 will clear your list. I've put some examples into a jsFiddle so you can see them in action (and so I coudl test they worked).
http://jsfiddle.net/HUvA2/3/ - The first button runs your method (You'll see it deletes every other entry) and the second runs my modified loop which you can see gets rid of them all. The "best" button just sets the list length to 0. :)
Basically this, but in pure javascript:
How to get 'value' of select tag based on content of select tag, using Nokogiri
I have a select list with a lot of countries/states, and I want to be able to select one based on what is between the <option> tags.
<option value="4783">Argentina</option>
(I know I could use the value, but each one is a random mesh of numbers, so I would have to collect each individual one - not economical)
It is a bit painful, but in pure JavaScript:
for(var i=0,sL=mySelObj.length;i<sL;i++){
if(mySelObj.options[i].text == myValue){
mySelObj.selectedIndex = i;
break;
}
}
Now that querySelector is available in most browsers you can use:
document.querySelector('select').selectedIndex =
document.querySelector('option[value=4783]').index;
Just 10 years late, but let's look at a new age solution.
If you are working in ES6+ here is a simple "one liner" to do the trick.
If you require broad compatibility, use a transpiler or another solution.
let $select = document.querySelector('select[name="countries"]');
let compareValue = 'Argentina';
$select.value = [...$select.options].filter(option => {
return option.innerText === compareValue;
})[0].value;
The true one-liner expression:
$select.value = [...$select.options].filter(option => option.innerText === compareValue)[0].value;
The textContent property lets you see what's inside the tag. Off my head and without testing, that should work:
function selectByDisplayValue(selectTag, displayValue)
{
var options = selectTag.getElementsByTagName('option');
for(var i = 0; i < options.length; i++)
if(options[i].textContent == displayValue)
{
options[i].selected = true;
break;
}
}
Where selectTag is the DOM object of your <select> tag, and displayValue is the display value of the option you want to select (for your example, "Argentina" it would be).