Reputation: 6363
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)
Upvotes: 2
Views: 2537
Reputation: 8631
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
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
Upvotes: 3
Reputation: 26940
<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;
}
}
Upvotes: 2