Toggle that cycles through 3 states - javascript

I need to create one button that will cycle through 3 states. For some reason that is incredible challenging to find. The functions in each state are simple style changes, example:
click 1: hide div
click 2: show hidden div, change bg-color
click 3: reset bg-color, change font
click 1: reset font, hide div
Any ideas? I can't use jquery (class assignment, not allowed)

Cou could use an array, with a function for each state change, and a counter variable which cycles through the possible states.
Then simply invoke the current state function through an click handler of the button.
Something like this could do it
var toggle = (function (el) {
var div = document.getElementById(el); //Get the div you want to change
var states = []; //The Array to hold the functions
var style = {} //Will be used to save the current style
for (var att in div.style) //Saves the style of the div *I was just lazy and did a for loop*
style[att] = div.style[att]
var current = 0; //The Counter
states[0] = function () { //The first state function
div.style["font-family"] = style["font-family"]
div.style.display = "none";
};
states[1] = function () {
div.style.display = "block";
div.style["background-color"] = "rgb(" + [rand(), rand(), rand()] + ")"; // [1,2,3] toString is "1,2,3"
};
states[2] = function () {
div.style["background-color"] = style["background-color"];
div.style["font-family"] = "Courier New";
}
function rand() { //ONly to return a random number for a random bgcolor
return ~~(Math.random() * 255)
}
return function () { //The function which cycles through the states
states[current]() //Invokes the current statechange function
current = (current + 1) % (states.length); //Increments the counter and uses modulo to cycle
}
})("div");
document.getElementById("click")
.addEventListener("click", toggle);
Heres an example on JSFiddle
Update:
I modified it a bit and commented the changed code, this should be able of changing the states of multiple elements on a page
function rand() {
return~~ (Math.random() * 255);
}
var makeToggle = function (states, elements) { // I Changed it to makeToggle, The first argument accepts an array of states to cycle through, the second either an array of elements, or an array of objects with the element property (and an optional state function)
var current = 0; //Sets the counter to zero
for (var i = 0, ilen = elements.length; i < ilen; i++) {
if (!elements[i].element) { //check if you passed an Object with the `element` Property
elements[i] = {
element: elements[i] //If it was an array, the arrays element will be set to an object
}; //to support arrays only
}
elements[i].style = {}; //to save the original style in the object
for (var att in elements[i].element.style) {
elements[i].style[att] = div.style[att]; // saves it
}
}
function doForElements() { //Invokes either the state function passed with an element, or the general statefunction
for (var i = 0, ilen = elements.length; i < ilen; i++) {
var state = elements[i].states;
if (state && typeof state[current] === "function") state = state[current];
else state = states[current];
state(elements[i].element, elements[i].style); //Invokes the function with the element as first parameter and the original style as second
}
}
return function () { //Returns the function for the click handler
doForElements();
current = (current + 1) % (states.length); //cycles the current state counter
};
};
var states = []; //Here the General State change functions get defined
states[0] = function (div, style) {
div.style["font-family"] = style["font-family"];
div.style.display = "none";
};
states[1] = function (div, style) {
div.style.display = "block";
div.style["background-color"] = "rgb(" + [rand(), rand(), rand()] + ")";
};
states[2] = function (div, style) {
div.style["background-color"] = style["background-color"];
div.style["font-family"] = "Courier New";
};
var elements = [].slice.call(document.getElementsByTagName("div")); //To actually get an Array of the NodeList (all divs on the page)
elements[4] = { //changes the 5th element (which should receive a special statechange function)
element: elements[4],
states: {
1: function (div, style) { //Use an Objects property to pass an single state change instead of an array with functions
div.style.display = "block";
div.style["background-color"] = "yellow";
}
}
};
var toggle = makeToggle(states, elements); //sets the function for the click handler to toggle
//Pass an Object with the Elements and an optional st
document.getElementById("click")
.addEventListener("click", toggle); //binds the function
Heres a JSBin to try it out

<div id="mydiv" data-state="0"></div>
<input type="button" id="mybutton" value="Change State" />
var states = {
1: [{ name: 'display', value: 'none'}],
2: [{ name: 'display', value: 'block'}],
3: [{ name: 'background-color', value: 'white'}, { name: 'prop', value: 'val' }]
}
window.onload = function(){
var mydiv = document.getElementById('mydiv');
var mybutton = document.getElementById('mybutton');
mybutton.onclick = function (){
var num = parseInt(mydiv.getAttribute('data-state'));
num = num < 3 ? ++num : 1;
var nameValues = states[num];
for(var i = 0; i < nameValues.length; i++)
mydiv.style[nameValues[i].name] = nameValues[i].value;
}
}

Related

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.

vanilla javascript: element.close() not working on element

This is a Sudoko generator I'm programming in vanilla javascript:
Fiddle with code
Nicer looking full screen fiddle
If you click on one of the fields, a popup will be shown with 3x3 fields from 1 to 9. The problem is this popup can't be closed anymore, although I'm applying the close dialog.
The code how I'm generating the Sudoku board:
// create sudoku
function tableCreate() {
var body = document.getElementsByClassName("frame")[0];
var containerDiv = body.appendChild(document.createElement('div'))
containerDiv.className = 'container';
// create single cells with numbers
function createInnnerCells(parent, xx, yy) {
for (var x = 1; x <= 3; x++) {
for (var y = 1; y <= 3; y++) {
var abc = function () {
var div = parent.appendChild(document.createElement('div'))
var X = y+yy;
var Y = x+xx;
var id = 'x' + [X] + 'y' + [Y];
var cellValue = sudoku[X][Y]['value'] || '';
div.style.background = sudoku[X][Y]['background'] || 'white'
div.className = 'cell';
div.id = id;
var popover = createDialog(id);
popover.onclick = function() {
popover.close();
};
div.onclick = function() {
popover.show();
};
div.appendChild(popover);
div.appendChild(document.createTextNode(cellValue));
};
abc();
}
}
}
// create big cells for 3x3 single cells
for (var i = 0; i <= 6; i+=3) {
for (var j = 0; j <= 6; j+=3) {
var div = containerDiv.appendChild(document.createElement('div'))
div.className = 'block';
createInnnerCells(div, i, j);
}
}
}
Note that I apply the close() function to each cell:
popover.onclick = function() {
popover.close();
};
The code how I create the popup:
// create dialog
function createDialog(position){
var dialog = document.createElement('dialog');
dialog.id ='window_'+ position;
var dialogblock = dialog.appendChild(document.createElement('div'));
dialogblock.className = 'dialogblock';
for (var z = 1; z <= 9; z++) {
var div = dialogblock.appendChild(document.createElement('div'));
div.className = 'dialogcell';
div.id = position + 'z'+ z;
div.appendChild(document.createTextNode(position));
}
dialog.onclick = function() {
dialog.close();
};
return dialog;
}
I applied the close() dialog here as well
dialog.onclick = function() {
dialog.close();
};
I don't know why show() is working, but close() not?
DOM events bubble up the DOM through its parents. In your code, the dialog is a child of div. Therefore, a click event happens on dialog and then again on div which means you're closing and then opening the dialog.
You can stop the propagation of the event by using event.stopPropagation.
You can change your code like this:
popover.onclick = function(e) {
e.stopPropagation();
popover.close();
};
and
dialog.onclick = function(e) {
e.stopPropagation();
dialog.close();
};
modified your fiddle: http://jsfiddle.net/p40oahkd/9/
There's no method close() on the element you are trying to hide. You should either do element.style.display = "none" if you need to hide. Or do the following:
dialog.addEventListener('click', function() {
this.remove();
});
Check out this edit to your fiddle.

for loop with array for background colors

This code takes the background color from an array of divs and colors another div with it. My problem is that I can put any value from 0-2 (0-2 being the array value of the original div) in task1[i].style.background but when I put i so that the loop gives me the value specific to the one moused over it brakes. I cannot use JQuery, JavaScript only.
EDIT: I expect task1[i].style.background to be equal to the background color of the specific div it hovered over from the loop running through the array.
var task1 = document.querySelectorAll('.t1_colors');
var task1background = document.querySelector('#task1');
var white = function() {
task1background.style.background = 'white';
}
for (var i = 0; i < task1.length; ++i) {
task1[i].addEventListener('mouseover', function () {
colorize();
});
}
//Something wrong with i
var colorize = function() {
task1background.style.background = task1[i].style.background;
}
for (var i = 0; i < task1.length; ++i) {
task1[i].addEventListener('mouseout', function() {
white();
});
}
What do you expect the value of i to be when you use it in colorize?
task1background.style.background = task1[i].style.background;
When colorize executes inside mouseover event listener, all loops have already terminated because i value reached task1.length.
You have to capture the current i value.
Change it to
for (var i = 0; i < task1.length; ++i) {
task1[i].addEventListener('mouseover', (function (index) {
return function() { colorize(index); };
})(i));
}
and
var colorize = function(index) {
task1background.style.background = task1[index].style.background;
}

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);

Categories

Resources