How to pass variable value from one function to another - javascript

I am taking a class in javascript and this is my final project. So I am new to javascript.
The application asks a user for a name, color and number of "boxes" to create. After they are created, the user can click on a box to get a message that states its ID, NAME, COLOR and the top position (XPOS) and left position (YPOS) of the box.
What I get now is all correct, except for XPOS and YPOS comes up as undefined. If I replace the this.xpos and this.ypos in the display() function to just xpos and ypos, I get the values of the last box created.
It seems (to my untrained eye) that the creation of the ID and the XPOS, YPOS are similar and should work the same. So why does the ID show correctly and not the 2 position variables? Any help would be appreciated.
var name;
var id;
var color;
var amount;
var xpos = 0;
var ypos = 0;
function Box(id, name, color, xpos, ypos) { //element constructor function
this.id = id;
this.name = name;
this.color = color;
this.xpos = xpos;
this.ypos = ypos;
}
var box
var boxes = [];
var counter = 0;
window.onload = init;
function init() {
var generateButton = document.getElementById("generateButton");
generateButton.onclick = generate;
var clearButton = document.getElementById("clearButton");
clearButton.onclick = clear;
}
function generate() {
var dataForm = document.forms.data; //create var for the form collection
var nameInput = document.getElementById("name"); //get text input for name of Amazing Boxes
name = nameInput.value;
if(name == null || name == "") { //check to see if the input box is empty
alert("***Please enter valid text for a name***"); //if it is empty, alert user
nameInput.focus();
return;
}
var colorList = dataForm.elements.color; //get color choice for Amazing Boxes
color = colorList.value;
var radioPick = dataForm.elements.amount; //get the choice for number of Amazing Boxes
for(var i = 0; i < radioPick.length; i++) {
if (radioPick[i].checked) {
amount = radioPick[i].value;
}
}
if(amount == null || amount == "") { //test to make sure the user has checked an amount
alert("***Please choose an amount of boxes to be generated***");
return false;
}
else {
while(amount > 0) {
var sceneDiv = document.getElementById("scene");
xpos = Math.floor(Math.random() * (sceneDiv.offsetWidth-101)); //get a random number for box position
ypos = Math.floor(Math.random() * (sceneDiv.offsetHeight-101)); //get a random number for box position
id = counter;
var div = document.createElement("div"); //create new div element
div.setAttribute("id", id); //give the new div an id = to the var id
div.setAttribute("class", "box"); //give the new div a class = to box
div.innerText = name; //set text on box to the name
sceneDiv.appendChild(div); //make the new div a child element of the scene div
div.style.left = xpos + "px";
div.style.top = ypos + "px";
div.style.backgroundColor = color;
div.onclick = display;
counter ++; //increment counter var to get different box ids
amount--;
}
}
dataForm.reset();
nameInput.focus();
}
function display() {
alert("The ID of this box is " + this.id + " and it is named " + name + ". It's color is " + color +
" and is located at " + this.xpos + " and " + this.ypos + ".");
}

Because of the following code:
div.onclick = display
The display function runs in the context of the div object in response to a click event. At this time, the this.id property (think div.id) contains the value you assigned to it. But the other values: this.xpos and this.ypos don't contain the values you are intending to be assigning to them.
Your code looks like it wants to run display in the context of a Box object. You need to create a new Box at some point and assign values to it. One way you might do this is by replacing:
div.onclick = display
with:
div.box = new Box(id, name, color, xpos, ypos);
div.onclick = function() { display.call(this.box); };
This is not the cleanest way to do what you want but it show you some mechanisms, and you will hopefully be able to use it as a starting point.

Related

Unable to target JS generated button within an array with a function, getting "Cannot read property 'setAttribute' of null" error

I'm simply trying to cause a specific button background to change when I click it.
I generated 100 buttons (in the hopes of making a simple game, later on) as an array through a for loop, while assigning an id and a distinct function call (based on the incrementing 'i' value of the loop), and am unable to actually cause the background to change upon clicking, getting the followed error instead
"Uncaught TypeError: Cannot read property 'setAttribute' of null
at showOptions (brain.js:31)
at HTMLButtonElement.onclick (index.html:15)"
My code goes as follows
var btn = [];
function generateBoard() {
for (i = 0; i < 100; i++) {
var modulo = i % 10;
var up, forLeft;
btn[i] = document.createElement("BUTTON");
var element = document.getElementById("body");
//btn[i].innerText = "CLICK ME";
element.appendChild(btn[i]);
btn[i].style.backgroundColor = "white";
btn[i].id = i;
btn[i].style.width = "50px";
btn[i].style.height = "40px";
btn[i].style.position = "absolute";
btn[i].style.top = modulo * 100;
btn[i].style.left = Math.floor(i / 10) * 100;
btn[i].x = (i + 10) % 10;
btn[i].y = Math.floor(i / 10);
document
.getElementById(btn[i].id)
.setAttribute("onclick", "showOptions(i)");
btn[i].innerText = btn[i].id;
console.log(
btn[i].id +
" " +
btn[i].style.left +
" " +
btn[i].style.top +
" " +
btn[i].x +
" " +
btn[i].y
);
}
}
generateBoard();
function showOptions(i) {
document.getElementById(i).setAttribute("style", "background-color: red;"); //this is line 31
}
in console.log I actually get the correct digit as btn[i].id, oddly enough.
the (index.html:15) line of the error is simply
</html>
There are multiple issues with your code.
You should get body element with getElementsByTagName, which returns array. So pick first element.
Setting onclick attribute should use value of i not text i.
var btn = [];
function generateBoard() {
for (i = 0; i < 100; i++) {
var modulo = i % 10;
var up, forLeft;
btn[i] = document.createElement("BUTTON");
var element = document.getElementsByTagName("body")[0];
//btn[i].innerText = "CLICK ME";
element.appendChild(btn[i]);
btn[i].style.backgroundColor = "white";
btn[i].id = i;
btn[i].style.width = "50px";
btn[i].style.height = "40px";
btn[i].style.position = "absolute";
btn[i].style.top = modulo * 100;
btn[i].style.left = Math.floor(i / 10) * 100;
btn[i].x = (i + 10) % 10;
btn[i].y = Math.floor(i / 10);
document.getElementById(btn[i].id).setAttribute('onclick', 'showOptions(' + i + ')');
btn[i].innerText = btn[i].id;
console.log(btn[i].id + " " + btn[i].style.left + " " + btn[i].style.top + " " + btn[i].x + " " + btn[i].y);
}
}
generateBoard();
function showOptions(i) {
document.getElementById(i).setAttribute("style", "background-color: red;"); //this is line 31
}
I've reworked your code, fixing a few issues. I assume you have an element with id body, which is distinct from the body element, which can be accessed through document.body.
var buttons = [];
function generateBoard(){
for(i = 0; i < 100; i++) {
var modulo = i % 10;
buttons[i] = document.createElement("BUTTON");
document.getElementById("body").appendChild(buttons[i]);
//buttons[i].innerText = "CLICK ME";
buttons[i].style.backgroundColor = "white";
buttons[i].id = i;
buttons[i].style.width = "50px";
buttons[i].style.height = "40px";
buttons[i].style.position = "absolute";
buttons[i].style.top = modulo * 100;
buttons[i].style.left = Math.floor(i / 10) * 100;
buttons[i].x = (i + 10) % 10;
buttons[i].y = Math.floor(i / 10);
buttons[i].addEventListener('click', function(event) {
// This code is run when the button is clicked
// Note I am passing the element, rather than an id
showOptions(this);
});
buttons[i].innerText = i;
console.log(buttons[i].id + " " + buttons[i].style.left + " " + buttons[i].style.top + " " + buttons[i].x + " " + buttons[i].y);
}
}
generateBoard();
function showOptions(button){
button.style.backgroundColor = "red";
}
Rather than assigning and attempting to use an ID attribute you can target the clicked element by referring to a property of the event itself. If you were to analyse the event ( use console ) you would notice several properties - the target allows access to the element itself which helps simplify the showOptions function. In this instance you could also simply use this to refer to the button itself from within showOptions - which would make it even simpler - such as this.style.backgroundColor='red';
let bttns=[];
const generateBoard=function( s=100 ){
const showOptions=function(e){
e.target.style.backgroundColor='red';
};
for( let i=0; i < s; i++ ){
/* create a new button and add properties */
let bttn=document.createElement('button');
bttn.setAttribute('id',i);
bttn.setAttribute('data-x',((i+10)%10));
bttn.setAttribute('data-y',Math.floor(i/10));
bttn.style.left=( Math.floor( i / 10 ) * 100 )+'px';
bttn.style.top=( ( i % 10 ) * 100 )+'px';
bttn.style.width = '50px';
bttn.style.height = '40px';
bttn.style.position = 'absolute';
bttn.innerText=i;
/* bind event listener */
bttn.addEventListener('click', showOptions );
/* add to the DOM */
document.body.appendChild( bttn );
/* if it is important to have in an array... */
bttns.push( bttn );
}
}
document.addEventListener('DOMContentLoaded',(e)=>{
generateBoard( 100 );
})
Adding arbitrary attributes to elements is not best practise - rather than assigning x and y you should make use of dataset attributes instead - so data-x and data-y would be correct.

Reset changes to Array

I am using a function to determine the color and brightness of a dynamic number of divs. Whenever I enter that div with my mouse, it a) is assigned a random color and b) becomes 10% darker. Now, when I re-run the function, I would expect all of the div's brightness (i.e. the values in the array "brightness") to be reset to 1. However, the browser keeps their last value (i.e. they remain dark), instead of reseting them. Why is that?
Best regards
Beni
//randomColor function is taken from http://www.devcurry.com/2010/08/generate-random-colors-using-javascript.html //
function randomRgb(value) {
col = "rgb("
+ randomColor(255) * value + ","
+ randomColor(255) * value + ","
+ randomColor(255) * value + ")";
}
function randomColor(num) {
return Math.floor(Math.random() * num);
}
function resetColorOfBoxes() {
boxes = document.querySelectorAll('div');
boxes.forEach(box => box.style.backgroundColor = "white");
}
function resetBrightness() {
brightness.forEach(brightness[i] = 1);
}
function promptEntry() {
let userInput = prompt("How many rows would you like?", "Enter a number");
if (isNaN(userInput)) {
alert("That's not a valid entry. Try again");
promptEntry();
}
else {
createGrid(userInput);
}
}
function createGrid(numberOfRows) {
resetColorOfBoxes();
let gridTemplateColumns = 'repeat('+numberOfRows+', 1fr)'
var container = document.getElementById("container");
container.style.gridTemplateColumns = gridTemplateColumns;
container.style.gridTemplateRows = gridTemplateColumns;
let brightness = [];
let i = 0;
let numberOfBoxes = numberOfRows**2;
/*Create boxes*/
for (i; i < numberOfBoxes ; i++) {
brightness[i+1] = 1;
console.log(brightness);
var div = document.createElement("div");
div.classList.add(i+1);
document.getElementById("container").appendChild(div);
div.addEventListener("mouseenter", function () {
var className = this.className;
brightness[className] = brightness[className] - 0.1;
console.log(brightness[className]);
randomRgb(brightness[className]);
this.style.backgroundColor = col;
});
}
}
let btn = document.getElementById("start")
btn.addEventListener("click", promptEntry)
<h1>Etch-a-sketch</h1>
<button id="start">Start</button>
<div id="container"></div>
You have the function resetBrightness() but it is not used anywhere in the code you posted. So that could be a potential issue.
However, assuming that you are using it, you are declaring let brightness = []; inside your createGrid() function which will render the array useless if used outside the function because it is a local variable used by createGrid()
To solve your issue, you can either modify your resetBrightness() function to accept the brightness array as a parameter and modify the array within the function or set the array as a global variable.

assign parameter value of an object javascript

I have been looking at this code for a long time trying to figure this out, but I am having no luck. This issue is that I want to assign a value to the parameter boxId. When I click on a box in the webpage an alert will come up displaying that id. I have tried many things, but nothing seems to work. I'm a beginner, so I feel at this point there just must be something that I don't know how to do.
constructor function:
function Box (boxId, name, color, number, coordinates) {
this.boxId = boxId;
this.name = name;
this.color = color;
this.number = number;
this.coordinates = coordinates;
}
global variables:
var boxes = [];
var counter = 0;
var boxId = 0;
init function:
window.onload = init;
function init() {
var generateButton = document.getElementById("generateButton");
generateButton.onclick = getBoxValues;
var clearButton = document.getElementById("clearButton");
clearButton.onclick = clear;
}
function to get values and create new boxes:
function getBoxValues() {
var nameInput = document.getElementById("name");
var name = nameInput.value;
var numbersArray = dataForm.elements.amount;
for (var i = 0; i < numbersArray.length; i++) {
if (numbersArray[i].checked) {
number = numbersArray[i].value;
}
}
var colorSelect = document.getElementById("color");
var colorOption = colorSelect.options[colorSelect.selectedIndex];
var color = colorOption.value;
if (name == null || name == "") {
alert("Please enter a name for your box");
return;
}
else {
var newbox = new Box(boxId, name, color, number, "coordinates");
boxes.push(newbox);
counter++;
var boxId = counter;
}
addBox(newbox);
var data = document.getElementById("dataForm");
data.reset();
}
function that adds boxes to the page:
function addBox(newbox) {
for (var i = 0; i < newbox.number; i++) {
var scene = document.getElementById("scene");
var div = document.createElement("div");
div.className += " " + "box";
div.innerHTML += newbox.name;
div.style.backgroundColor = newbox.color;
var x = Math.floor(Math.random() * (scene.offsetWidth-101));
var y = Math.floor(Math.random() * (scene.offsetHeight-101));
div.style.left = x + "px";
div.style.top = y + "px";
scene.appendChild(div);
div.onclick = display;
}
}
function to display alert when box is clicked:
function display(e) {
var a = e.target;
alert(a.counter);
}
function to clear boxes:
function clear() {
var elems = document.getElementsByClassName("box");
for ( k = elems.length - 1; k >= 0; k--) {
var parent = elems[k].parentNode;
parent.removeChild(elems[k]);
}
}
All of the other functions work just fine. I keep running into the id showing up as "undefined" when I click it, or the counter displaying "0" in the console log, for everything I've tried.
You can do it like this.
First, in addBox() embed boxId as an tag's attribute like this:
div.setAttribute('data-boxId', newbox.boxId);
Then in display() you can retrieve it back:
alert(e.target.getAttribute('data-boxId'));
Please tell if you do not prefer this approach and I will post an alternative (closure things).
Edit: Add jsfiddle example http://jsfiddle.net/runtarm/8FJpU/
One more try. Perhaps if you change:
var boxId = counter;
to
boxId = counter;
It will then use the boxId from the outer scope instead of the one defined in the function getBoxValues()

how to use an event object to dispaly information about a DOM element

I want to be able to click on a box (the boxes are created through code, and receive values from a form) in the webpage and display information about the box. I am working on a display() function that uses an event object and an alert to display information about the box. So far, I've had multiple odd failures in my attempt to do this, which leads me to believe that I'm not accessing object attributes correctly. I'm a beginner, so this could be really obvious, but thanks for the help.
constructor function:
function Box (counter, name, color, number, coordinates) {
this.counter = counter;
this.name = name;
this.color = color;
this.number = number;
this.coordinates = coordinates;
}
Global variables:
var boxes = [];
var counter = 0;
Init function:
function init() {
var generateButton = document.getElementById("generateButton");
generateButton.onclick = getBoxValues;
var clearButton = document.getElementById("clearButton");
clearButton.onclick = clear;
}
Function that gets values from the form:
function getBoxValues() {
var nameInput = document.getElementById("name");
var name = nameInput.value;
var numbersArray = dataForm.elements.amount;
for (var i = 0; i < numbersArray.length; i++) {
if (numbersArray[i].checked) {
number = numbersArray[i].value;
}
}
var colorSelect = document.getElementById("color");
var colorOption = colorSelect.options[colorSelect.selectedIndex];
var color = colorOption.value;
if (name == null || name == "") {
alert("Please enter a name for your box");
return;
} else {
var newbox = new Box(counter, name, color, number, "coordinates");
boxes.push(newbox);
counter++;
/*for(m = 0; m < boxes.length; m++) {
counter.newbox = boxes[m];
}*/
}
addBox(newbox);
var data = document.getElementById("dataForm");
data.reset();
}
function that assigns attributes to the boxes:
function addBox(newbox) {
for (var i = 0; i < newbox.number; i++) {
var scene = document.getElementById("scene");
var div = document.createElement("div");
div.className += " " + "box";
div.innerHTML += newbox.name;
div.style.backgroundColor = newbox.color;
var x = Math.floor(Math.random() * (scene.offsetWidth-101));
var y = Math.floor(Math.random() * (scene.offsetHeight-101));
div.style.left = x + "px";
div.style.top = y + "px";
scene.appendChild(div);
div.onclick = display;
//console.log(newbox);
//shows all of the property values of newbox in the console
//console.log(div); shows that it is an object in the console
//console.log(div.hasAttribute(number)); says false
}
}
display function:
function display(e) {
// alert(e.target); says its an html object
//alert(e.target.className); works - says "box"
//alert(e.target.hasAttribute(name)); says false
}
I've included some of the things i've found in comments.
The event object only gives you the name not a reference to the element. So... a couple of things.
First if you want to be browser agnostic you want something like (e.srcElement is for IE):
var x = e.target||e.srcElement;
Then get a reference to the element and do what you want:
var refToElement = document.getElementById(x.id);

Make eventlisteners unique?

So I have a problem where the eventlisteners I setup all happen to work with the same variable.
This is how it looks like:
// Prepare tooltips
for (var i = 0; i < document.getElementsByClassName("tooltip").length; i++) {
var tooltip = document.getElementsByClassName("tooltip")[i];
var input = document.getElementsByName(tooltip.id.substr(8))[0];
var offsetTop = 0;
var tmp = input;
while (tmp != null) {
offsetTop += tmp.offsetTop;
tmp = tmp.offsetParent;
}
offsetTop -= 130;
var offsetLeft = (input.offsetParent.offsetLeft + input.scrollWidth) + 50;
tooltip.innerHTML += "<div class='corner'></div>";
tooltip.style.top = offsetTop + "px";
tooltip.style.left = offsetLeft + "px";
input.addEventListener("focus", function() { document.getElementById(tooltip.id).style.display = "block"; });
input.addEventListener("blur", function() { document.getElementById(tooltip.id).style.display = "none"; });
}
In the last two lines I set the eventlisteners.
So whenever I focus an input field, no matter which one tooltip.id is always the same.
I checked the input.id before its different in every loop.
Javascript is a funny language :)
In each loop you're declaring a function which uses a reference to the variable tooltip.
Since you use the variable many times: its value changes but the reference remains the same.
When the function executes, it uses the reference (which has the last value).
Here is the solution:
(I recommend calling the method 'document.getElementsByClassName("tooltip")' only once since it causes DOM traverse.
==== CODE STARTS HERE
var toolips = document.getElementsByClassName("tooltip");
for (var i = 0; i < toolips.length; i++)
{
var tooltip = toolips[i];
var input = document.getElementsByName(tooltip.id.substr(8))[0];
var offsetTop = 0;
var tmp = input;
while (tmp != null)
{
offsetTop += tmp.offsetTop;
tmp = tmp.offsetParent;
}
offsetTop -= 130;
var offsetLeft = (input.offsetParent.offsetLeft + input.scrollWidth) + 50;
tooltip.innerHTML += "<div class='corner'></div>";
tooltip.style.top = offsetTop + "px";
tooltip.style.left = offsetLeft + "px";
// assign tooltip id to the input
input.tooltipId = tooltip.id;
// add event listeners
input.addEventListener("focus", function() { document.getElementById(this.tooltipId ).style.display = "block"; });
input.addEventListener("blur", function() { document.getElementById(this.tooltipId).style.display = "none"; });
}
==== CODE ENDS HERE

Categories

Resources