I have an array of 10 elements. At first I want to shuffle the array elements and display them (my current code does this). Then I want to show an alert message if any of the displayed array elements are clicked. This is the thing I am having problem with. If, I am doing onclick, it's showing me only 1 array element instead of all the 10 array elements and also showing the following error in console Uncaught TypeError: Cannot set property 'onclick' of undefined. My code is (I am using only javascript):
<script>
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
var myArray = ['1','2','3','4','5','6','7','8','9','10'];
newArray = shuffleArray(myArray);
for (i=0;i<newArray.length;i++) {
var p = document.write(newArray[i] + "<br >");
p.onclick = showAlert; // this is showing me only 1 array element and also showing error in concole
}
function showAlert() {
alert("onclick Event detected!")
}
</script>
You need to create an actual element, rather than trying to bind events to strings:
for (i=0;i<newArray.length;i++) {
var p = document.createElement('p');
p.innerHTML = newArray[i];
p.onclick = showAlert;
document.body.appendChild(p);
}
http://jsfiddle.net/92fRb/1/
Cannot set property 'onclick' of undefined
Here's your problem... p is undefined.
Why?
Because document.write does not return a DOM element. Actually it does not return anything at all (or, returns undefined).
You want to:
Use document.createElement() to create an element.
Use p.innerHTML to set the content (inner HTML) of your new element.
Append your new element to the document.
Your code can be revised as suggested in #Johan's answer.
for (i=0;i<newArray.length;i++){
var p = document.write("<span class='no'>"+newArray[i] + "</span><br />");
}
function showAlert(){
alert("onclick Event detected!")
}
for(i=0;i<document.getElementsByClassName("no").length;i++){
document.getElementsByClassName("no")[i].onclick=showAlert;
}
Related
I am working on a project where the user decides how many buttons are there, and then each button triggers a different function.
What I have done is: I have an input that asks "Enter the number of buttons" and a "Submit" button. When the "Submit" button is clicked, the value of variable n is set to the user-defined value.
I then have the following code (assuming the user has set n to 10):
for (i = 0; i < 10; i++) {
var x = document.createElement('div');
x.id = i;
x.innerHTML = i;
x.onclick = function() {alert(i)};
document.body.appendChild(x);
}
What I want is, that when div with id i is clicked, the pop-up message says i. But what has ended up happening is, that no matter which div I click, the pop-up message always says 10.
Why is this happening? How to fix this?
The only thing you need to change is the assignment of i inside the for-loop. Use let to define a locally-scoped variable. Also, use textContent instead of innerHTML for simple text. It is recommended to used const/let rather than var.
for (let i = 0; i < 10; i++) {
const x = document.createElement('div');
x.id = i;
x.textContent = i;
x.onclick = function() { alert(i) };
document.body.appendChild(x);
}
By iterating over your NodeList elements, you can take this next approach.
First of all, append all your created divs in your HTML and continue by looping through the elements list by document.querySelectorAll("div")
That way you select all elements and then assign an addEventListener to each one of the items. On your alert function, print this.id and it will return you the number of the id of the element which corresponds to your i index.
It would be the same also if you just put the whole addEventListener function inside the other loop.
I just separated both so you can understand it better.
for (i = 0; i < 10; i++) {
var x = document.createElement('div');
x.id = i;
x.innerHTML = i;
document.body.appendChild(x);
}
let divs = document.querySelectorAll("div");
for(var a= 0; a < divs.length; a++){
divs[a].addEventListener("click", function(){
alert(this.id);
});
}
You can use getAttribute to read the id of the elements. I show you an example:
for (i = 0; i < 10; i++) {
var x = document.createElement('div');
x.id = i;
x.innerHTML = i;
x.onclick = function(e) {
let dataId= e.currentTarget.getAttribute("id")
alert(dataId)
};
document.body.appendChild(x);
}
Explanation:
getAtrribute() returns the value of an html attribute as a string, just enter the name of the attribute in braces to get the value. In this case I entered "id" which is the value we want to retrieve.
Also to get the value of the element where you click I use currentTarget, to accurately retrieve the value of the div that the iteration creates. If you use target, and inside the div you have more elements, this code will generate an error. Therefore, it is important to use currentTarget for this application.
This happen because after the initialisationn the function will look for the value of i which is always 10 (once it has been initialized)
EDIT
As explained by #Mr. Polywhirl in his better answer, you can use let in your for loop to declare your variable locally instead of globally.
To solve your problem you can pass an event to the function (the click event) and the getting your value.
It can be done with either :
The value of the content (as you did with x.innerHTML)
Adding an id to the div and getting this id
Example :
for (i = 0; i < 10; i++) {
var x = document.createElement('div');
x.id = i;
x.innerHTML = i;
x.onclick = function(event) {
alert(event.target.innerHTML) // with the content
alert(event.target.id) // with the id
};
document.body.appendChild(x);
}
so I wrote a script to display 5 random arrays, but the page doesn't display anything.
here's the code:
<html>
<head>
<script>
function start(){
var arr(5),result;
result=document.getElementById("arraying");
result="<p>";
for(var i=0; i<5;i++){
arr[i]=Math.floor(Math.random()*10);
result+="arr["+i+"]= "+arr[i]+"</p><p>";
}
result+="</p>";
}
window.addEventListener("load",start,false);
</script>
</head>
<body>
<div id="arraying"></div>
</body>
</html>
I tried removing result=document.getElementById and write document.getElementById.innerHTML=result in the end of the function but didn't work. what's the error?
You cannot use the same variable for different purposes at the same time. First you assign a DOM element to result, and immediately on the next line you overwrite result with a string.
Build a string htmlStr inside your loop, and when that is done, assign this string to result.innerHTML property:
function start() {
let arr = [],
result, htmlStr = '';
result = document.getElementById("arraying");
htmlStr += "<p>";
for (let i = 0; i < 5; i++) {
arr[i] = Math.floor(Math.random() * 10);
htmlStr += "arr[" + i + "]= " + arr[i] + "</p><p>";
}
htmlStr += "</p>";
result.innerHTML = htmlStr;
}
window.addEventListener("load", start, false);
<div id="arraying"></div>
Looking at the code you seem to be missing some basic javascript concepts.
array size
This is probably your main issue:
var arr(5)
This does not make sense in javascript. Array length does not need to be predefined since all arrays are of dynamic length. Simply define an array like this:
var arr = []
Then later when you want to append new elements use push like this:
arr.push( Math.floor(Math.random()*10) )
adding html using innerHTML
There are different ways to dynamically inject html into your page. (It looks like) you tried to append the html as a string to the parent element. This is not possible.
You said you tried using innerHTML. That should work if used correctly.
A working implementation would work like this:
function start() {
var arr = []
var result = "<p>"
for(var i = 0; i < 5; i++) {
arr.push( Math.floor(Math.random()*10) ) // Btw this array isn't actually needed.
result += "arr[" + i + "] = " + arr[i] + "</p><p>"
}
document.getElementById("arraying").innerHTML = result
}
window.addEventListener("load", start, {passive: true});
adding html using createElement
A generally better way of dynamically adding html elements is via createElement.
This way you dont have to write html and are therefore less prone for making errors. It is also more performant and easier to integrate into javascript.
I think the best explaination is a commented implementation:
function start() {
var myDiv = document.getElementById("arraying") // get parent node
var arr = []
for(var i = 0; i < 5; i++) {
arr.push( Math.floor(Math.random()*10) )
var p = document.createElement("p") // create p element
p.innerText = "arr[" + i + "] = " + arr[i] // add text content to p element
myDiv.append(p) // append p element to parent element
}
}
window.addEventListener("load", start, {passive: true});
small tips
The let keyword works mostly the same as the var keyword, but is generally preferred because of some edge cases in which let is superior.
Fusing strings and variables using the plus operator is generally considered bad practice. A better way to do the string concatenation would have been
result += `arr[${i}] = ${arr[i]}</p><p>`
I would like to create JS code that put next elements from array in form after some action, for example after click.
I want to run this code in console (firebug, firefox F12) while im visiting some website. I created test example of code that should work on stackoverflow but it doesn't, why?
Example - stackoverflow.com:
After clicking in header area (id #question-header), my next element from array should be displayed in search form input (name q).
Code doesnt work when I run it in firefox console.
var arr = ['foo', 'bar', 'baz'];
var i = 0;
function nextItem() {
i = i + 1; // increase i by one
i = i % arr.length; // if we've gone too high, start from `0` again
return arr[i]; // give us back the item of where we are now
}
window.addEventListener('load', function () {
document.getElementsByName('q').value = arr[0]; // initial value
document.getElementById('question-header').addEventListener(
'click', // we want to listen for a click
function (e) { // the e here is the event itself
document.getElementsByName('q').value = nextItem();
}
);
});
How I can fix it?
input elements are self-closing tags therefore textContent does not work. It's value can be accessed by value attribute:
document.getElementById('search').value = nextItem();
UPDATE:
According to question update I think the main problem in your code is that you attach listener on page load. You don't need to do it when you paste code to browser console. Here is the working code on SO:
var arr = ['foo', 'bar', 'baz'];
var i = 0;
function nextItem() {
i = i + 1;
i = i % arr.length;
return arr[i];
}
// getElementsByName returns array of elements so access it by index
document.getElementsByName('q')[0].value = arr[0];
// because header contains inner link, add listener on it and prevent it from navigating
var header = document.getElementById('question-header');
var headerLink = header.getElementsByTagName('a')[0];
// when you pass code directly to console, page is (in most cases) already loaded
// you don't need to add listnener to 'load' event
headerLink.addEventListener(
'click',
function (e) {
document.getElementsByName('q')[0].value = nextItem();
e.preventDefault(); // prevent link to navigate
}
);
document.getElementsByName('q').value = arr[0]; // initial value
This is wrong (should give an error), because document.getElementsByName('q') is a nodeList (similar to array) that has no value property. Most probably you have only one element name q, so you can use [0] to get the first (and only) one.
document.getElementsByName('q')[0].value = arr[0];
// ...
document.getElementsByName('q')[0].value = nextItem();
And I'd suggest storing this element in a variable so that you don't load it every time you need it but only once.
var el = document.getElementsByName('q')[0];
// ...
el.value = arr[0];// or nextItem();
This is working:
HTML
<div id="header">
</div>
<form>
<input type="text" id="search" size="16" maxlength="16" />
</form>
JS
var arr = ['foo', 'bar', 'baz'];
var i = 0;
var search = document.getElementById('search');
function nextItem() {
i = i + 1; // increase i by one
i = i % arr.length; // if we've gone too high, start from `0` again
return arr[i]; // give us back the item of where we are now
}
window.addEventListener('load', function () {
search.textContent = arr[0]; // initial value
document.getElementById('header').addEventListener(
'click', // we want to listen for a click
function (e) { // the e here is the event itself
search.value = nextItem();
}
);
});
CSS
#header{
width:500px;
background-color:#444444;
color:#fff;
text-align:center;
padding-top:40px;
padding-bottom:40px;
}
FIDDLE
Why does this code throw an error in the console reading TypeError: pizzaBox.querySelector is not a function. (In 'pizzaBox.querySelector('h6')', 'pizzaBox.querySelector' is undefined)?
function addToppingsToAll (toppings)
{
var pizzaBoxHolder = document.getElementById("PizzaBoxHolder");
var PizzaBoxList = pizzaBoxHolder.childNodes;
for ( var i = 0 ; i < pizzaBoxList.length ; i++ )
{
var pizzaBox = pizzaBoxList[i];
toppingList = pizzaBox.querySelector('h6');
toppingList.textContent = "You have " + toppings " on your pizza";
}
}
There are at least three isssues in your code:
You are probably iterating through some text nodes which don't have a .querySelector() method.
You are not initializing your for loop iteration variable i
You have an undeclared variable lineBoxList you are attempting to use.
You can simplify things by just using .querySelectorAll() and letting the selector do more of the work for you.
function addToppingsToAll (toppings) {
var toppingItems = document.querySelectorAll("#PizzaBoxHolder h6");
for (var i = 0; i < toppingItems.length; i++) {
toppingItems[i].textContent = "You have " + toppings " on your pizza";
}
}
querySelector is a method that appears on HTML Element Nodes. One or more of the childNodes must be some kind of node that is not an Element (such as Text Nodes or Comment Nodes).
This is probably most easily resolved with:
var PizzaBoxList = document.querySelector("#PizzaBoxHolder > *");
This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 8 years ago.
I have several textboxes with the class output. I would like to be able to print their values as a plain HTML list in a div with ID combined. Right now, I have the following code:
function doCombine() {
document.getElementById('combined').innerHTML =
document.getElementsByClassName('output').value + ",";
}
Yet, when I run the function, i get the error message undefined,. When i add a [0] before .value, it works, but only the value of the first textbox is showing up. I read somewhere that [i] will show all the values, but that doesn't seem to work.
What am I doing wrong?
getElementsByClassName
Returns a set of elements which have all the given class names. When called on the document object, the complete document is searched, including the root node. You may also call getElementsByClassName on any element; it will return only elements which are descendants of the specified root element with the given class names.
So you should be doing
var elements = document.getElementsByClassName('output');
var combined = document.getElementById('combined');
for(var i=0; i < elements.length; i++) {
combined.innerHTML += elements[i].value + ",";
}
getElementsByClassName returns an array-like object, not a single element (notice the plural in the name of the function). You need to iterate over it, or use an array index if you just want to operate on the first element it returns:
document.getElementsByClassName('output')[0].value + ","
getElementsByClassName returns a set of elements. You need to iterate over it:
var elems = document.getElementsByClassName("output");
for(var i=0; i<elems.length; i++) {
combined.innerHTML += elems[i].value + ",";
}
That's why adding [0] works, because you are accessing the first object in this set.
This function will return ALL the elements with that name, because"name" attribute is not unique, so it returns an list (nodeList, to be exact).
To print out ALL the values, you need to add a loop. Something like
var finalvar = "";
var arr = document.getElementsByClassName('output');
for (i=0;i<arr.length;i++) {
finalval = finalval + arr[i].value;
}
document.getElementById('combined').innerHTML = finalval
getElementsByClassName will return a set of elements. Refer: https://developer.mozilla.org/en-US/docs/Web/API/document.getElementsByClassName#Summary. Some browsers return HTMLCollection and some browsers return NodeList. https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection#Browser_compatibility But they both have length property and item method in common. So you can iterate like this.
function doCombine()
{
var listOfOutputElements = document.getElementsByClassName('output');
var combinedItem = document.getElementById('combined');
for (var i = 0; i < listOfOutputElements.length; i += 1) {
combinedItem.innerHTML += listOfOutputElements.item(i).innerHTML;
}
}
Try this :
<script type="text/javascript">
function doCombine()
{
var combined = document.getElementById('combined');
var nodeList = document.getElementsByClassName('output');
var nodeListLength = nodeList.length;
for (i=0;i<nodeListLength;i++) {
combined.innerHTML += nodeList[i] + ',';
}
</script>
getElementsByClassName returns an NodeList. So you won't be able to call the value method on it. Try the following:
function doCombine() {
var combined = document.getElementById('combined');
var outputs = document.getElementsByClassName('output');
for(var i=0; i<outputs.length; i++){
combined.innerHTML += outputs[i].value + ',';
}
}
http://jsfiddle.net/FM3qH/