I am learning Jquery and Javascript from web examples. I have a good working knowledge but some code still trips me up. The following code is used for a shopping cart to hide the check out button and replace with a div displaying a message about minimum cart requirements. There is a part of the code throwing me off though.
function getCollectionCount() {
var totalCollectionCount = 0;
var collection = $('td[alt*="Collection"]');
for (var i = 0; i < collection.length; i++) {
var curVal = $(collection[i]).find("select").val();
if (curVal != undefined){
totalCollectionCount += parseInt(curVal);
}
}
What does this part mean?
var collection = $('td[alt*="Collection"]');
td[alt*="Collection"] selects all <td> elements whose alt attribute contains Collection, such as:
<td alt="Collection"></td>
<td alt="CollectionFoo"></td>
<td alt="BarCollection12324343"></td>
but not
<td></td>
<td alt=""></td>
<td alt="Foo"></td>
Side note: this is a pretty basic question that could easily be answered by read the jQuery selectors API documentation:
element selector
attribute-contains selector
Please do try to research before you ask!
This is a jQuery attribute selector clause. It's selecting any td element which has an atrtibute named alt whose string contains the value Collection.
Contains Selector: http://api.jquery.com/attribute-contains-selector/
jQuery has a number of useful attribute selectors. Here is the main reference page for them. Very much worth the read if you're just getting started with jQuery
http://api.jquery.com/category/selectors/attribute-selectors/
That code returns every td element whose "alt" attribute contains "Collection".
http://api.jquery.com/attribute-contains-selector/
jQuery is full of these funky shortcuts that take forever to learn, so I always keep a copy of jQuery in action on my desk at all times :)
This code can be rewritten more simply and briefly like this:
function getCollectionCount() {
var totalCollectionCount = 0;
$('td[alt*="Collection"] select').each(function() {
var val = this.value || "0";
totalCollectionCount += parseInt(val, 10);
});
return(totalCollectionCount);
}
And, this is how it works:
Initialize totalCollectionCount to 0
Find all td elements that have the string "Collection" in the alt attribute and then find select elements within that td
Iterate over all elements found that match the above condition
Initialize a local variable with either the value of the select object or "0" if there is no value or it's empty
Turn that value into a number (must pass the radix to parseInt so it won't guess) and add it to the sub-total so far.
return the total we found.
Related
I've gone through many SO threads, I can't seem to find a working solution.
All I'm trying to do is when the page loads, the site pushes all elements with the ".home" class into the array arr. Then, the script parses through each element in the array and tries to match it with a string. For example, right now all I have is a check to see if the element has the words "Boston" in it, in which case I want to make the image source for ".homeimage" the linked imgur link. I'm aware it's not wise to host images on imgur for these reasons, I'm just trying to check if it works. Below this test I have some redundant code I was practicing with that I found in a SO thread, changing the color of text to gray. I figured changing attributes is the same.
my html code:
<td colspan = "3"width=400px class = "home"><b><%= game.home %></b></td>
<td colspan = "3"><img style="width:150px;height:128px;" class = "homeimage"></td>
my javascript/jquery code:
<script>
var arr=[];
$(document).ready( function(){
$(".home").each(function(){ arr.push($(this));});
for(i = 0; i < arr.length; i++){
if(arr[i].indexOf "Boston" != -1){
$('.homeimage img').attr("src","http://i.imgur.com/s5WKBjy.png");
}
}
$.each(arr,function(key,val){
val.css('color','gray')}); //something redundant i was testing out
});
</script>
additional questions:
When I have multiple image with the .homeimage class, and multiple checks to determine the image source, will it make all of the images in the .homeimage class that src at the end? So whatever the last image that gets checked is the image src for all of the images with the ".homeimage" class? I don't want that. How can I uniquely make each image? Make a custom id instead of a class for each div? Also, does this script have to be below the html in question? Or does that not matter
Thanks for the future advice you all.
// I don't quite understand what you want to do.
// Since you type too much, and make no highlights.
// but here are somethings I found:
var arr = []; // this array is going to contain all tags (like td) with class '.home'
if(arr[i].innerHTML.indexOf("Boston") != -1) { } // indexOf() won't work on DOM element
// then arr[0] must be a DOM element, so why you call .indexOf("Boston") on it?
// next, $('.homeimage img') all return DOM element with class 'homeimage' or with tagName 'img'
$('img.homeimage'); // this may what you want to do.
// Alright, I try to give you an answer.
// this oImgUrl works as a map from some ((String))-->((img url))
var oImgUrl = {
'Boston': 'http://another.imageurl.com/boston.png',
'NewYork': 'http://another.imageurl.com/newyork.png'
};
// I take your "arr" unchanged
// this will test every element in arr
// if carry String like 'Boston' or 'NewYork'
// then find the img tag (img.homeimage) in it.
// then apply URL string to img tag
for (var i=0, length=arr.length; i < length; i++) {
if(arr[i].innerHTML.indexOf("Boston") != -1) {
arr[i].find('img.homeimage').attr('src', oImgUrl['Boston']);
continue;
}
if(arr[i].innerHTML.indexOf("New York") != -1) {
arr[i].find('img.homeimage').attr('src', oImgUrl['NewYork']);
continue;
}
}
example html:
<td class='home'>Welcome to Boston!<img class='homeimage'></td>
<td class='home'>Welcome to New York!<img class='homeimage'></td>
answers:
Question 1: Custom ID?
JavaScript will find these two td.home and add them into arr.
then, apply different image url to img tag
according to innerHTML of the td tag.
when doing this, you don't need to set each img tag an unique ID.
Question 2: Script place below html?
No, you don't have to.
You hold all thses script in docuement ready function
so, they will only work when HTML DOM is ready.
in another words, no matter where you place this script,
they will be invoked after Every Tag is ready.
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.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I have little big problem. I try almost everything and I am not able to select an element from table if I know some commented text. I think that this is not possible. For example
<table id='table1'>
<tr>
<td>
Some text
</td>
<td>
<!-- someid="id301" -->
</td>
</tr>
<tr>
<td>
Some next text
</td>
<td>
<!-- someid="id303" -->
</td>
</tr>
<table>
OK. So I only know for example this value someid="301" or someid="302". And I need to get parent element for example to change color of this by jquery. I really tested everything and I am not able to get tr element. Thank you for your help.
You can filter out the content of each TDs but honestly i don't see any purpose for that:
var someValue = 'someid="id303"';
$('table').find('td').contents().filter(function(){
return this.nodeType === 8 && this.nodeValue.indexOf(someValue) != -1;
}).closest('tr').css('background-color', 'red');
-jsFiddle-
If you need that string to setup something, then generally it is not a good practice to put that string in comment.
I would suggest you to put that someid as a data attribute in td element
<td data-someid="id301">...</td>
then you can access it by $td.attr("data-someid") or even use it in jQuery selector
$('td[data-someid="id301"]').css('background-color', 'green');
You can use jQuery Comments() Plug-in or
<input type="hidden" id="id301">
Try this:
<script>
var tableId = 'table1';
var table = document.getElementById(tableId);
var trs = table.getElementsByTagName('tr');
// Pattern for searching for the string like someid="id[somenumbersinarow]"
var pattern = /someid=\"id[0-9]+\"/
// Pattern for searching for numeric value
var numericPattern = /[0-9]+/
var innerHTML, tr;
for (var i = 0, amountOfTrs = trs.length; i < amountOfTrs; i++){
tr = trs[i];
innerHTML = tr.innerHTML;
/* Searching for the comment with "someid" string, turning it into string
(it will be the only string because there will be a single element in the array),
then again search for numbers, convert to string and try to parse it) */
console.log(parseInt(innerHTML.match(pattern).toString().match(numericPattern)));
}
</script>
This script properly works if there's only one comment per the TR element
pattern is a regular expression object. It can be used with modifiers such as g (search for ALL the matches in the text), for instance: /[0-9]+/g. In this case you'll get an array which may have a length greater than 1.
In the example I provided the modifier isn't used, so toString() method will return a string in this case. If you go further and try to get a number by another pattern, then you can use parseInt to extract number (id)
Be aware that if the match() method doesn't find anything it returns null, so before invoking function like in my example, you should either strongly know that this comment exists or perform some additional check.
In this JSFiddle (with the problem code commented out) the first click in an empty cell sets a value in a hidden input and sets the bgcolor of the cell to green. A click in a second empty table cell sets the value of another hidden input and changes the second cell bgcolor to red.
Now, based on feedback from another SO question I have tried to implement a check by looping through an array (All the commented out code) of all td's in the table to see if onclick, any cell already has a bgcolor set to green/red respectively and if true, set the bgcolor to empty/blank to allow for the NEW cell selection to get the bgcolor , so there should always be only 1 green block and 1 red block. Can someone explain to me how I am implementing the loop and check wrong and not getting the expected result.
the array looping works here -jsfiddle when not part of the existing code. But when I add it to code where it's needed, it does not work.
HTLM
<div id="starget"></div>
<div id="etarget"></div>
<table width="100%" id="test">
<thead>
<tr>
<th>Tech</th>
<th>0800</th>
<th>0900</th>
<th>1000</th>
<th>1100</th>
<th>1200</th>
</tr>
</thead>
<tr>
<td>Bill</td>
<td>XXX</td>
<td onclick="res(0900,this);"></td>
<td>XXX</td>
<td>XXX</td>
<td onclick="res(1200,this);"></td>
</tr>
</table>
SCRIPT
var x = 0;
var click = 0;
/* tdElements = document.getElementsByTagName("td"); */
/* I have tried the tdelements array inside and outside of the function */
function res(zz,el) {
if (click == 0) {
/* for(var key in tdElements) {
if (tdElements[key].style.backgroundColor=="green") {
tdElements[key].style.backgroundColor="";
}
} */
document.getElementById('starget').innerHTML=zz;
click = 1;
el.style.backgroundColor='green';
}
else {
/* for(var key in tdElements) {
if (tdElements[key].style.backgroundColor=="red") {
tdElements[key].style.backgroundColor="";
}
} */
document.getElementById('etarget').innerHTML=zz;
click = 0;
el.style.backgroundColor='red';
}
}
If you call .getElementsByTagName, you are not getting back an array! You are getting back a live HTMLCollection element, which contains some other items that you cannot ignore when using a for.. in loop. Heres where a for loop wil come in handy as your live HTMLCollection contains a length you can use!
/* HTMLCollections come with a length attribute we can use
* as every item is numbered, but in a object-style key-value pair */
for(var i = 0; i < tdElements.length; i++){
/* This was tripping up your code - the `item` function to fetch an
* element at the index defined in the HTMLCollection*/
tdElements.item(i).style.backgroundColor = "green"
}
This will turn all the backgrounds green. Now you will have to amend your code, but thats how it works. More info here: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection
What you really want to be doing is handling this with a class, rather than a style. The reason is that you can very easily pull up all of the elements that have a specific class using getElementsByClassName (note: in IE 9 and later).
Without deviating from your approach TOO much (I did make it a little more efficient :) ), your code would need to change to:
CSS:
.firstClick {background-color: green;}
.secondClick {background-color: red;}
Script:
var click = 0;
function res(zz,el) {
var currentlyClickedEls, currClass, divTarget;
if (click === 0) {
currClass = "firstClick";
divTarget = "starget";
click = 1;
}
else {
currClass = "secondClick";
divTarget = "etarget";
click = 0;
}
// get all the elements with the appropriate class
currentlyClickedEls = document.getElementsByClassName(currClass);
// remove that class from those elements
while (currentlyClickedEls.length > 0) {
currentlyClickedEls[0].className = "";
}
// add the class to the clicked element
el.className = currClass;
document.getElementById(divTarget).innerHTML = zz;
}
With this approach, on the click, the first thing that you would do is find all of the elements with the class firstClick or secondClick (based on the value of click) and remove the class. Since there should only ever be one, at the most, that makes for a VERY short loop to go through (but would also remove the class from more than one element, if that were to happen somehow).
EDIT:
So, you wouldn't have noticed it in your code, since you don't ever have more than one instance of each of the two classes, but, as somethingthere pointed out, we are dealing with a LIVE collection of DOM elements, so, as you remove the classes from the elements (the element that you removed them from, falls out of the collection.
For example, on the third click of your table, there will be one element in the collection that is returned by document.getElementsByClassName(currClass); = HTMLCollection[td.firstClick] (in the console). But, once you've removed that class from the element, it no longer matches the criteria, so the collection becomes empty (i.e., HTMLCollection[]).
So, while it works for your situation (because it stops on i = 1 because that is still "not less than" the new length of the collection (which is now 0).
However, in other situations, that might not be the case . . . for example, if there were 2 elements returned, after the first one had its class removed, i would change to 1, but the length would also drop to 1 and the loop would stop, without processing the second element.
So the approach really needs to be changed to handle that behavior correctly. I've done that by changing these lines:
for (i = 0; i < currentlyClickedEls.length; i++) {
currentlyClickedEls[i].className = "";
. . . to these lines:
while (currentlyClickedEls.length > 0) {
currentlyClickedEls[0].className = "";
This way, the element that has it's class removed is always the first one in the collection and the while loop checks to make sure that there is still an element in the collection, before it attempts to remove the class.
So, long story, short, when dealing with an HTML Collection, as somethinghere pointed out, you are dealing with a live collection of DOM elements, so, if you make a change that would affect an individual elements inclusion in that Collection, it WILL update the Collection accordingly and you need to be able to account for that in your code.
I'm actually creating a script for a webpage.
I have not the possibility to modify the actual markup.
Here is the HTML Source Code :
<span id="spnTrNdCnt">
<span class="fufw" fldrnm="ibx" id="spnFldrNm">IBX</span>
<span id="spnUC">(
<span id="spnCV">2</span>
)</span>
</span>
I want to get the textContent for the span id equals spnCV.
The problem is that this code is present many times in the HTML Page. So each time i try to get the textContent (document.getElementById('spnCV').textContent) it gives me the last value of the last item.
I would like to know if there is any way to get the value of this first item (the first span which have the id spnCV) !
Thanks a lot for any kind of help.
querySelectorAll should in theory work for CSS selectors, which don't care about the restriction regarding unique ids.
var spans = document.querySelectorAll('#spnCV');
var length = spans.length;
for (var i=0; i<length; i++) {
console.log('Span ' + i, spans[i].textContent);
}
Working JsBin: http://jsbin.com/oriSiNa/1/edit
EDIT:
Since you only need the first span, then it's even easier:
var value = document.querySelector('#spnCV').textContent;
working demo fiddle
var ele = document.getElementsByTagName('span');
for(var i=0;i<ele.length;i++)
{
if(ele[i].id=='spnCV')
{
// when i=0 it will be the first node
// do something
var text = (ele[i].innerText || ele[i].textContent);
alert(text);
}
}
ID's must be unique as is mentioned before, but since you cannot change the source, please check out firstchild, assuming that you can add some js somehow.