I have several heading tags with IDs named 'Cat1', 'Cat2', 'Cat3' etc.
<h3 id="Cat1"></h3>
After an Ajax call a function receives an array of category names and this array is passed into another function which I want to use to place each of the categories from the array, into each of the heading tags. This is what I have at present:
for(var x=0; x<category_array.length; x++) {
var word = category_array[x];
var identify = "Cat" + (x+1).toString();
document.getElementById(identify).innerHTML = word;
}
I am currently getting the following error:
Uncaught TypeError: Cannot set property 'innerHTML' of null
Firstly, why is this occuring and secondly, how can I successfully update my heading tags with each of the categories in the array?
EDIT: If I write 'Cat1' into the getElementById script, it will update the h3 tags. But, the variable identify does not seem to be read correctly.
The error is occurring because document.getElementById(identify) returns null, which happens when the element you're trying to find does not exist. Otherwise, your code looks correct.
Make sure you have enough Cat<N> elements on the page to fill. One way to debug this issue is to console.log(identify) then look at the HTML to see if you have all the elements that appear in the console.
Related
I am trying to scrape the following Javascript frontend website to practise my Javascript scraping skills:
https://www.oplaadpalen.nl/laadpaal/112618
I am trying to find two different elements by their xPath. The first one is the title, which it does find. The second one is the actual text itself, which it somehow fails to find. It's strange since I just copied the xPath's from Chrome browser.
from selenium import webdriver
link = 'https://www.oplaadpalen.nl/laadpaal/112618'
driver = webdriver.PhantomJS()
driver.get(link)
#It could find the right element
xpath_attribute_title = '//*[#id="main-sidebar-container"]/div/div[1]/div[2]/div/div[' + str(3) + ']/label'
next_page_elem_title = driver.find_element_by_xpath(xpath_attribute_title)
print(next_page_elem_title.text)
#It fails to find the right element
xpath_attribute_value = '//*[#id="main-sidebar-container"]/div/div[1]/div[2]/div/div[' + str(3) + ']/text()'
next_page_elem_value = driver.find_element_by_xpath(xpath_attribute_value)
print(next_page_elem_value.text)
I have tried a couple of things: change "text()" into "text", "(text)", but none of them seem to work.
I have two questions:
Why doesn't it find the correct element?
What can we do to make it find the correct element?
Selenium's find_element_by_xpath() method returns the first element node matching the given XPath query, if any. However, XPath's text() function returns a text node—not the element node that contains it.
To extract the text using Selenium's finder methods, you'll need to find the containing element, then extract the text from the returned object.
Keeping your own logic intact you can extract the labels and the associate value as follows :
for x in range(3, 8):
label = driver.find_element_by_xpath("//div[#class='labels']//following::div[%s]/label" %x).get_attribute("innerHTML")
value = driver.find_element_by_xpath("//div[#class='labels']//following::div[%s]" %x).get_attribute("innerHTML").split(">")[2]
print("Label is %s and value is %s" % (label, value))
Console Output :
Label is Paalcode: and value is NewMotion 04001157
Label is Adres: and value is Deventerstraat 130
Label is pc/plaats: and value is 7321cd Apeldoorn
I would suggest a slightly different approach. I would grab the entire text and then split one time on :. That will get you the title and the value. The code below will get Paalcode through openingstijden labels.
for x in range(2, 8):
s = driver.find_element_by_css_selector("div.leftblock > div.labels > div")[x].text
t = s.split(":", 1)
print(t[0]) # title
print(t[1]) # value
You don't want to split more than once because Status contains more semicolons.
Going with #JeffC's approach, if you want to first select all those elements using xpath instead of css selector, you may use this code:
xpath_title_value = "//div[#class='labels']//div[label[contains(text(),':')] and not(div) and not(contains(#class,'toolbox'))]"
title_and_value_elements = driver.find_elements_by_xpath(xpath_title_value)
Notice the plural elements in the find_elements_by_xpath method. The xpath above selects div elements that are descendants of a div element that had a class attribute of "labels". The nested label of each selected div must contain a colon. Furthermore, the div itself may not have a class of "toolbox" (Something that certain other divs on the page have), nor must it contain any additional nested divs.
Following which, you can extract the text within the individual div elements (which also contain the text from the nested label elements) and then split them using ":\n" which separates the title and value in the raw text string.
for element in title_and_value_elements:
element = element.text
title,value = element.split(":\n")
print(title)
print(value,"\n")
Since you want to practice JS skills you can do this also in JS, actually all the divs contain more data, you can see if you do paste this in the browser console:
labels = document.querySelectorAll(".labels");
divs = labels[0].querySelectorAll("div");
for (div of divs) console.log(div.firstChild, div.textContent);
you can push to an array and check only divs and that have label and return the resulted array in a python variable:
labels_value_pair.driver.execute_script('''
scrap = [];
labels = document.querySelectorAll(".labels");
divs = labels[0].querySelectorAll("div");
for (div of divs) if (div.firstChild.tagName==="LABEL") scrap.push(div.firstChild.textContent, div.textContent);
return scrap;
''')
I have the following code written in my .js file:
var tiles = document.querySelectorAll(".tile");
var tileNumbers = ["one", "two", "three", "four"];
for(var i = 0; i < tiles.length; i++){
var num = Math.floor(Math.random() * tileNumbers.lenth);
tiles.classList.add(tileNumbers[num]);
tileNumbers.pop(num);
}
The .tile are 4 <div>'s in the .html file, and I am trying to add a class each of the four tiles separately. The classes are held in tileNumbers. When I run the code in Chrome I get the error:
"Uncaught TypeError: Cannot read property 'add' of undefined."
I am pretty sure everything is spelled correctly. Please help!
You want to call add on the tile, but try to access the add function of the tiles array itself. This does not exist.
What you need to do is to access the add function of each individual tile. To do so, first get it:
var tile = tiles[i];
Then, change your call to
tile.classList.add(…);
(You could also omit the temporary variable tile, and use tiles[i].classList.add directly. But IMHO using a dedicated variable makes the code more clear to read.)
Another option, which may be even better, is to use forEach. Since you use the i only for accessing the current element and nothing else, you basically want to perform an action on each element. You can do it like this as well (and, for bonus points, this even protects you against off-by-one errors):
tiles.forEach(function (tile) {
// ...
});
Then, within the function body, you automatically have a variable tile that you can access in the way you want to.
That's it :-)
tiles[i].classList.add(tileNumbers[num]);
Not
tiles.classList.add(tileNumbers[num]);
I've got some JS code here. Basically, I am trying to change the ID of an element to some value from a previous variable.
Here's what I got so far;
function() {
var colorarray = [ "RANDOMCOLOR_0", "RANDOMCOLOR_1", "RANDOMCOLOR_2" ];
var RANcolorarray = colorarray[Math.rsound(Math.random() * (colorarray.length - 1))];
document.getElementsByClassName('RANDOMCOLOR').setAttribute('id', RANcolorarray);
}
This code throws an error in Chrome for line 4: Uncaught TypeError: undefined is not a function which is weird because JsLint finds no errors.
I also tried using the other way to setting id's;
document.getElementsByClassName('RANDOMCOLOR').id = RANcolorarray;
However, although this method does not throw an error on chrome or jslint - it does not work at all after inspecting the element.. :/
Any ideas?
document.getElementsByClassName('RANDOMCOLOR') returns a list of DOM nodes (even if there's only one match) so you can't just call .setAttribute() on the list as the list doesn't have that method.
You can either get the first item out of the list and call .setAttribute() on that one or use a for loop to iterate through the list and call it on all of them. Of course, since you're setting the id, you should not be setting multiple elements to the same id, so I'll assume you just want one element:
document.getElementsByClassName('RANDOMCOLOR')[0].id = RANcolorarray;
Or, a little more safe:
var elems = document.getElementsByClassName('RANDOMCOLOR');
if (elems && elems.length) {
elems[0].id = RANcolorarray;
}
I have an error in the following code and I can't find why...
Using UiApp I define a couple of ListBox like this in a for loop
var critGlist = app.createListBox().setName('critGlist'+n).setId('critGlist'+n).addChangeHandler(refreshGHandler).addChangeHandler(cHandlerG).setTag(listItem[n]);
I added a TAG to be able to retrieve a value in the handler function because when I add items to this list I do it like this :
for(var c=0;c<listItem[n].length;++c){
critGlist.addItem(listItem[n][c],c);// the returned value is c, the value shown is listItem[n][c]
}
Then in my handler function I retrieve the value c that is the index of an element of the array listItem[n]
Since I stored a stringified value of this array as a tag I have to retrieve the tag first and then using the index I get the desired value...
That's where it becomes problematic !
I tried the 3 following codes :
var idx = Number(e.parameter['critGlist'+c]);// this works and I get the index
var item = e.parameter.critGlist0_tag.split(',')[idx];// this also works for a fixed index (0 here) but I need to use it in a for loop so I tried the code below
var item = e.parameter['critGlist'+c]_tag.split(',')[idx];// this generates an syntax error
// error message :"Signe ; manquant avant l'instruction. (ligne 129, fichier "calculatrice Global")"
// which means : missing ; before statement (line 129...
Am I missing something obvious ? How should I write it differently ?
Obviously it is the underscore that is not accepted... but how could I not use it ?
Well, I have a few other possibilities to get the result I want (using a hidden widget for example or some other temporary storage of even let the listBox return the value instead of the index) but still I'd like to know why this syntax is wrong ...
I'm not asking for a different code (as mentioned before there are a lot of other ways to go) , just some explanation about what is wrong in this code and this #!##å»ÛÁØ underscore ;)
You will need to put the whole property within the brackets as below
var item = e.parameter['critGlist'+c+'_tag'].split(',')[idx];// this generates an syntax error
I am trying to make multiple text input fields share a value. The text fields are named StartDate#, where # is replaced with a number. I am trying to use the following code:
function SetTimeFrame(element) {
if (document.Labs.SameDate.checked==true) {
var Elements=document.Labs.elements.length;
for (var i=0; i<Elements; i++) {
if (document.Labs.elements[i].type=='select-one') {
document.Labs.elements[i].options[element.selectedIndex].selected=true;
document.getElementById("StartDate"+i).value = document.getElementById("StartDate0").value;
}
}
}
}
When I use this code I receive the following error:
Javascript error at position (45:7): 'document.getElementById[...]' is null or not an object
45:7 is the line:
document.getElementById("StartDate"+i).value = document.getElementById("StartDate0").value;
It appears that it isn't appending the value of "i" to "StartDate" as it iterates through the forloop. I know that I have "StartDate0" and "StartDate1" in the page, so I know they are not null.
I looked at a couple of other Stack Overflow questions similar to mine such as, getElementById with a variable plus an id name, and How to access an element by giving a variable in JSP using getElementById? but neither prooved to give a sucessful solution.
Any help given would be greatly appreciated.