Can't change class when using getElementsByClass - javascript

I've been going around in circles on this one. I have a PHP output that generates a series of DIV elements, and each has a class of L1, L2, L3, etc. These classes have a CSS that's basically blank so they display by default.
The problem is trying to change the class of each DIV to hide it, based on a simple onclick function. I'm using document.getElementsByClass to get (for example) just the DIV CLASS="L1" elements. But when I try to change the class, either with setAttribute('class', 'L1hide') or with className = "L1hide", nothing happens.
I know the HTML/CSS portion works, since I altered the PHP to generate "L1hide" instead of just "L1" and saw that the DIV CLASS="L1hide" elements were indeed hidden.
Is it because I'm trying to both grab the class and change the class at the same time? All the examples I see use getElementById, but this isn't practical for me since ID must be unique. I have zero, one, or more L1/L2/etc. class elements.
Here are some HTML code output by PHP:
<div class="L1"><h3>Owner</h3>
<table>
<tr><td>Jim Smith</td></tr>
</table>
</div>
<div class="L1"><h3>Executives</h3>
<table>
<tr><td>Harry Atkins</td></tr>
<tr><td>Galen Singh</td></tr>
</table>
</div>
<div class="L2"><h3>Managers</h3>
<table>
<tr><td>Andy Jones</td></tr>
<tr><td>Mary Thompson</td></tr>
<tr><td>Bill Murphy</td></tr>
</table>
</div>
And here is some javascript. This should change the image and hide the L1's. The image change DOES work (first 4 lines), but the style won't change (last 2 lines) despite my burnt offerings:
managmentImage.onclick = function() {
if (managmentLevel == "TOP1.png") {
document.getElementById("managementImage").setAttribute("src", "TOP0.png");
managmentLevel = "TOP0.png";
document.getElementsByClassName("L1").className = "L1hide";
divL1 = "L1hide";
} else { ...
Note: There is basically duplicate "else" code to change the image back, and to re-display the L1's. I only provided the first half of the "if" statement since the solution would also apply to the remaining "else" portion.

getElementsByClass is returning an array-like collection of nodes; those don't have className, individual nodes do.
You can't iterate it normally, because it is a live collection: if you change the class of the first element, the array shortens by one, so you will only process half of the nodes if you iterate from start to end. Iterating from end to start fixes this issue.
var elements = document.getElementsByClassName("L1");
console.log(elements);
var i = elements.length;
while (i--) {
elements[i].className = "L1hide";
}
.L1hide {
display: none;
}
<div class="L1"><h3>Owner</h3>
<table>
<tr><td>Jim Smith</td></tr>
</table>
</div>
<div class="L1"><h3>Executives</h3>
<table>
<tr><td>Harry Atkins</td></tr>
<tr><td>Galen Singh</td></tr>
</table>
</div>
<div class="L2"><h3>Managers</h3>
<table>
<tr><td>Andy Jones</td></tr>
<tr><td>Mary Thompson</td></tr>
<tr><td>Bill Murphy</td></tr>
</table>
</div>

getElementByClassName() returns an array.
You must iterate on it and then change the property.

Related

Copy divs with certain id's to targets with related id's

How do I copy the content from all divs with a certain class or a certain beginning of an id to a bunch of divs with related id's? The examples I found didn't go through multiple copies. I have a solution that is working ok but instead of calling each copy with the id I would like to make the logic better.
Here's what I have now.
HTML (handlebars template)
<!-- the id comes from handlebar content. There are many like this -->
<pre><code id="target-example-01" class='language-markup'></code></pre>
<!-- this content is put to place once handlebars has rendered the page -->
<div id="code-examples" style="display: none;">
<div class='code-example' id="source-example-01">
this is my a code example... there are many like me...
</div>
</div>
Javascript
var source = document.getElementById('source-example-01').innerHTML;
var target = document.getElementById('target-example-01');
if (target) target.innerHTML=source;
This works ok but I have 100 examples so I wouldn't like to have 300 lines of code to manually maintain just to copy the content. How do I go through all the divs with "code-example" class and copy their content to divs with a matching id. All the source divs will have the id="source-(example-identifier)" and all the target divs will have the id="target-(example-identifier)". I guess the class wouldn't be needed if the code would go through all items with the id beginning with "source-"
I would be old school and stick with using getElementsByClassName() but since the question is how to target divs will have the id="target-(example-identifier) you can use querySelectorAll()
document.querySelectorAll('div[id^="source-example-"]')
for more information about querySelectorAll()
Returns a list of the elements within the document (using depth-first pre-order traversal of the document's nodes) that match the specified group of selectors. The object returned is a NodeList.
So the output is very much like using getElementsByClassName()
If you have any questions please leave a comment below and I will reply as soon as possible.
How to target a specific tag with a class and id?
document.querySelectorAll('div.code-example[id^="source-example-"]')
You will still need to loop through the contend just like you would for returning elements by class name but this query selector will only return div elements with the class name code-example and contains source-example- in the id attribute.
function QuerySelector() {
var Selector=document.querySelectorAll('div.code-example[id^="source-example-"]');
for(var i=0; i<Selector.length; i++){
alert(Selector[i].innerHTML);
}
}
<div class="code-example" id="source-example-01">Content Line One. - with class and id </div>
<div class="code-example">Content Line Two. - with correct class but no ID</div>
<div class="code-example" id="source-example-02">Content Line Three. - with class and id </div>
<button onclick="QuerySelector()">Get</button>
I hope this helps. Happy coding!
How do I go through all the divs with "code-example" class and copy
their content to divs with a matching id
Assuming that index of code-example elements is same as that of targets, then try
var allCodeExamples = document.getElementsByClassName( "code-example" );
for ( var counter = 0; counter < allCodeExamples.length ; counter++ )
{
var index = ("0" + counter).slice( -2 );
var target = document.getElementById( "target-example-" + index );
target.innerHTML = allCodeExamples[counter].innerHTML;
}
You can make use of data attributes for this using jquery:
<!-- the id comes from handlebar content. There are many like this -->
<pre><code id="target-example-01" class='language-markup'></code></pre>
<!-- this content is put to place once handlebars has rendered the page -->
<div id="code-examples" style="display: none;">
<div class='code-example' id="source-example-01" data-target="#target-example-01">
this is my a code example... there are many like me...
</div>
</div>
$(function(){
var targetId;
$(".code-example").each(function(){
targetId = $(this).attr("data-target");
$(targetId).html($(this).html());
});
});
I realised it makes more sense to check which examples are on the page and fetch the content for their source id's. I added another class to the code-tags "target-example"
Here's the javascript (jquery would probably make it nicer looking)
var examples = document.getElementsByClassName('target-example');
var i;
var id;
for (i = 0; i < examples.length; i++) {
id = examples[i].id;
var source = document.getElementById('source-'+id).innerHTML;
if (source) examples[i].innerHTML=source;
}

Javascript get inner HTML text for span by class name

This is the basic format of the code the table is contained within a div named
<div class="leftCol">
.....
<tr id="my_cd">
<td><span class="agt_span">My Code</span></td>
</tr>
.....
</div>
I need to be able to get whatever text is contained within the span class, in this case I need to pull the text "My Code" and then add that into an array. Adding the text into an array is not the issue that's easy but I can't figure out how to pull the text. No matter what I try I can't get anything but an 'undefined' value.
How do I get the Inner HTML text value for a span by class name?
First Question solved thanks!!
Second question expand on first:
<div class="leftCol">
.....
<tr id="my_cd">
<td><span class="agt_span">My Code</span></td>
<td>
<div>
<select name="agt_drp" id="agt_drp" class="agt_drp">...</select>
</div>
</td>
</tr>
</div>
Let's say I have the select id "agt_drp" and I want to get the span class text. Is there any way to do that?
Jquery:
var test = $("span.agt_span").text();
alert(test):
Javascript:
http://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp
in vanilla javascript, you can use getElementsByClassName():
var htmlString = document.getElementsByClassName('agt_span')[0].innerHTML;
https://jsfiddle.net/ky38esoo/
Notice the index behind the method.
JQuery:
$('span.agt_span').text();
Pure JavaScript (you need to specify the position of your class element: [0] to get the first one):
document.getElementsByClassName('agt_span')[0].innerHTML;
If you have multiples elements with this class, you can loop on it:
var elts = document.getElementsByClassName('agt_span');
for (var i = 0; i < elts.length; ++i) {
alert(elts[i].innerHTML);
}
Though getElementsByClassName seems to be supported by all major browser, that is now argument to use it. To keep your code compatible and usefull, better use the W3C Standard DOM Level 3 Core. The Document IDL does not describe such a method there!
So please use
var table = document.getElementById("my_cd"); /* id is unique by definition! */
var spans = table.getElementsByTagName("span");
var txt;
for(i in spans) {
if(spans[i].getAttribute("class").contains("agt_span")){
txt = spans[i].firstChild; /* a span should have only one child node, that contains the text */
}
}
return txt;
This method isn't perfect, as you actually need to split the spans[i].getAttribute("class").split(" ") on space chars and check if this array contains "agt_span".
By the way: innerHTML is no DOM Attribute too. But you can implement anything in a compatible and flexible way using W3C DOM and you will be sure to write effective and compatible code.
If the js programmers had used the W3C Documents and if there weren't no Internet Explorer to break all those ECMAScript and W3C rules, there would never have been that many incompatibilities between all those browser versions.

Javascript showing tr when the list item has a specific value

I've to following question. I've a multiple listitem and i want show the three hidden tr i've set to display 'hidden' when the list item has a specific value.
the form elements have the following set up
<tr>
<td><label>bla</label></td>
<td><input id='bla'></td>
</tr>
i use the following javascript.
function checkValue(){
var verhuurdVan = document.getElementById('verhuurd_van_datepicker_field').parentNode.parentNode.style.display='none';
var verhuurdTot = document.getElementById('verhuurd_tot_datepicker_field').parentNode.parentNode.style.display='none';
var beschikbaar = document.getElementById('beschikbaar_verhuur_1').parentNode.parentNode.style.display='none';
var list = document.getElementById('productstatus_id_ListOn');
for(var i=0; i<list.options.length; i++){
if(list.options[i].value == '1'){
here the table rows has to be set to display block;
}
}
Ignore the HTML id name's because this is parsed by the CMS i use.
thanks in advance.
Without knowing the exact layout of your DOM it's not possible to give the exact lines of code required. However, it seems quite clear from the code block you posted that the elements are being hidden by lines 3-5.
Consequently, within your inner if block, all you have to do to make the elements reappear is to set the style of the corresponding elements back to 'block', e.g.:
document.getElementById('verhuurd_tot_datepicker_field').parentNode.parentNode.style.display='block';
Which of the three nodes you actually activate is up to you to decide, but that's how you would go about it.

How to create an effect higher up in the view than the logic that determines it?

I have a simple if statement in my view that returns x = 0 or 1. Based on this simple result, I want to change the styling of the div that contains the entire section.
<div>
conditional that returns x=1 vs x=0 (and a few displayed items)
based on this loop, restyle the div
</div>
Let's say, if x = 1, I want to make background-color:rgb(210,215,220);
How can I accomplish this? I am not experienced with Javascript but I'm sure any code required would be very simple. Thank you!
Add an ID to your DIV like:
<div ID="colorThis">
conditional that returns x=1 vs x=0 (and a few displayed items)
based on this loop, restyle the div
</div>
Then do this:
if(x==1)
{
​document.getElement​ById('colorThis').style.background = 'rgb(210,215,220)'​​​​;​​​​​​​​​​​​​​
}
<script>
if(x==1)
{
$("#divid").css("background-color","rgb(210,215,220)");
}
</script>
Include jQuery, and give the div's id to divid

Another jquery dynamic selector

I just asked a question here about selecting all id's of the form id_* where * is any string and id is the id of the element. I got a great working solution:
$("*[id^=" + id + "_]").each(function() {... // id is the element name
I need to take this one step further now:
All of my ids will be of the form: a_b_c ... where a b and c are arbitrarity strings (that do NOT contain a ''). So, now rather than selecting all the elems of the form id_* where * could be a_b_c ... I need only to select 1 level deep: i.e. all elems of the form id_a. So in other words it stops at the next ''. Is this possible?
As an example:
If my id is: first
And there exist id's: first_second, first_second_third
It will select only first_second
Sounds like you are storing too many values in the id of the field. With HTML5 we now have data- attributes.
Perhaps, you should be making use of data- attributes something like this to link them?
<div id="a">
</div>
<div id="b0" data-parentId='a'>
</div>
<div id="b1" data-parentId='a'>
</div>
<div id="b2" data-parentId='a'>
</div>
<div id="c" data-parentId='b1'>
</div>
It will still validate, as any non-standard attribute starting with data- will be considered valid.
See: http://ejohn.org/blog/html-5-data-attributes/
Your jQuery selectors can then make use of this new attribute, rather than trying to parse strings
Something like this would select all of a's children
var childrenOfA = $("div[data-parentId='a']);
What I ended up doing (and I'm open to faster implementations) is:
$("*[id^=" + id + "_]").each(function() {
//here I simply split the id and test the size of the array
//if its too large (i.e. too deep in the tree), I return true (to continue
// to the next iteration):
var row = $(this);
var split = row.attr('id').split("_");
if(split.length > SOME_PREDETERMINED_VAL)
return true;
//code here
});
I am not totally happy with this since it still traverses all elements (or would it do this anyway regardless of the filter in the each() function??).
This doesn't give you the whole solution, but you could try the attribute prefix selector or the attribute starts with selector
That will allow you to select any descendant of an element:
$("[id^='a_b_']").each(....
Will think how to remove the grandchildren etc, but this might get you started.
EDIT
I have just found that a similar question was asked about jQuery wildcards - this looks as if it will do what you need.
It seems like you are seriously overcomplicating this task. Let's say your structure is currently like this:
<div id="a">
<div id="a_b">
<div id="a_b_c">
</div>
</div>
</div>
Why don't you just do something along these lines...
<div id="a">
<div class="b">
<div class="c">
</div>
</div>
</div>
So, if I JUST wanted #a .b I would do:
$("#a .b").not("#a .c").show();
Makes it a bit more semantic and readable as well. Am I understanding what you're trying to do? Might need to shed a bit more light on what exactly you're doing
The obvious solution is changing your document, for example instead of
<div id="a"></div>
<div id="a_b"></div>
<div id="a_b_c"></div>
you could write
<div id="a" class="l1"></div>
<div id="a_b" class="l2"></div>
<div id="a_b_c" class="l3"></div>
and then select $('.l2[id^=a_]'). If that is not an option, you could try some sort of sieve scheme:
var set = $('[id^='+id+'_]'), i = 0;
while (i < set.length) {
var e = set.eq(i);
if (e.attr('id').substr(id.length+1).match('_')) {
set = set.not(e);
} else {
i++;
}
set = set.not($('[id^='+e.attr('id')+'_]'));
}
(I haven't tested, so there might be errors, and I'm not sure not is the one that subtracts from a result set, but you get the idea.)
It depends on the document structure and browser whether this will be actually faster than the naive implmentation of simply walking through the while set and skipping everything with two _ in the id. (High number of branches per node helps, and it will be probably faster on browsers which have a native implementation of the CSS3 prefix selector which jQuery can call on.)
update: fixed some mistakes. The logic might change depending on your structure, e.g. the innermost if branch is unnecessery if foo_bar always precedes foo_bar_baz.

Categories

Resources