I'm creating an if statement that fires different functions. Is there a better way I can accomplish this functionality?
$("#slide01-bttn").click(function() {
if ($("#slide-1").is('#featured_ul li:nth-child(1)'))
{
alert("First Child");
moveSlideFirstChild();
}
if ($("#slide-1").is('#featured_ul li:nth-child(2)'))
{
alert("Second Child");
moveSlideSecondChild();
}
if ($("#slide-1").is('#featured_ul li:nth-child(3)'))
{
alert("Third Child");
moveSlideThirdChild();
}
if ($("#slide-1").is('#featured_ul li:nth-child(4)'))
{
alert("Fourth Child");
moveSlideFourthChild();
}
});
You could of course simplify your code to this
var pos = $("#slide-1").prevAll().length;
switch (pos)
{
case 0:
...
break;
case 1:
...
break;
...
default:
...
break;
}
But it seems that you should optimize your methods that move child elements to have just one function instead of N so you could do a simple call:
var elem = $("#slide-1");
// pass position AND element because you'll likely use it inside
moveSlideChild(elem.prevAll().length, elem);
You could of course just pass in the element and get position inside. And save some element selections because you're doing many many jQuery element selector calls. This would make your code significantly faster and optimised.
$("#slide01-bttn").click(function() {
var slide = $("#slide-1");
var index = $('#featured_ul li').index(slide);
var position = ["First", "Second", "Third", "Fourth"][index];
if (position) {
alert(position+" Child");
// assuming they are global functions:
window["moveSlide"+position+"Child"]();
}
});
If your functions are local variables, you still could do
var fn = [moveSlideFirstChild, moveSlideSecondChild, moveSlideThirdChild, moveSlideFourthChild][index];
if (fn) fn();
But I would really recommend to paramatize your moveSlide function, so that you only need one that takes the element to move as an argument:
moveSlide(slide);
I think the best thing that you could do would be to paramatize your moveSlide functions
function moveSlide(childNumber) {
//Grab the child element you are looking for
//Move it code
}
Really you should be able to do something with the ids on the li to simplify it even more. I'm not sure how the HTML is layed out. if you could provide a fiddle we could probably make more progress.
You could use a switch to get the job done:
$("#slide01-bttn").click(function() {
var pos = $("#slide-1").prevAll().length;
switch(pos)
{
case 0:
case 1:
moveSlide(pos);
break;
default:
//code to be executed if pos is different from case 0 and 1
}
}
function moveSlide(childNumber) {
//Grab the child element you are looking for
//Move it code
}
If moveSlideNChild() all do similar things, it would be best to apply some abstraction to reduce the amount of code. You can then call
var $list = $('#featured_ul li'); // set here for cacheing
var $slide = $("#slide-1"); // set here for cacheing
$("#slide01-bttn").click(function() {
var position = $list.index($slide);
if( position >= 1 && position <= 4) { // only want 1 through 4
alert('Child ' + position);
moveSlideChild(position);
}
}
Related
I have 3 "dice" objects created from this custom constructor:
function Dice() {
this.value = 0;
this.keep = false;
this.roll = function() {
this.value = Math.floor(Math.random()*6)+1;
};
}
Then, inside function rollOnce(), I have 3 HTML buttons inside a document.getElementById("paragraph1").innerHTML command that will display each dice's value as follows:
function rollOnce() {
(...)
document.getElementById("paragraph1").innerHTML =
'<button id="diceOne" class="unkept" onclick="keepDice(this.id)">'+dice1.value+'</button> ' +
'<button id="diceTwo" class="unkept" onclick="keepDice(this.id)">'+dice2.value+'</button> ' +
'<button id="diceThree" class="unkept" onclick="keepDice(this.id)">'+dice3.value+'</button> ';
}
Now, function keepDice(diceId) will set attribute class="kept" for each dice/button that has been clicked.
The next thing I want to do is to know which dice variable (dice1, dice2, dice3) has been clicked (in order to keep their value by doing diceN.keep = true;. Because after that there will be another round of the game in which only those dice which are "unkept" will get another diceN.roll() call. But my knowledge is still very limited and I only know how to access (HTML only) elements by using document.getElementsBy(...) (this is the HTML DOM, right? I'm currently learning this at W3Schools).
I have not yet learned about jQuery, AngularJS and all the other cool webdev stuff. So if it is possible to answer using only Javascript it would be much appreciated (even if other libs would make it easier! It's a bonus if there are alternative solutions and I would be happy to learn too!). Is this possible at all?
Thanks in advance,
Maybe something like class="kept-'+dice1.keet+'" onclick="keepDice(1)"
then
function keepDice(index){
dices[index].keep = true;
turns--;
if (turns > 0) {
rollOnce()
}
}
Try this:
function keepDice(id) {
var whichDice;
switch(id) {
case 'diceOne':
whichDice = dice1;
break;
case 'diceTwo':
whichDice = dice2;
break;
case 'diceThree':
whichDice = dice3;
break;
}
whichDice.keep = true;
}
If you stored your dice in an associative array like this:
dice['diceOne'] = new Dice();
dice['diceTwo'] = new Dice();
dice['diceThree'] = new Dice();
you would create the buttons almost the same way
<button id="diceOne" class="unkept" onclick="keepDice(this.id)">dice["diceOne"].value</button>
you could then write your dice function like this
function keepDice(id)
{
dice[id].keep = true;
document.GetElementById(id).setAttribute("class","kept");
//...
}
I came back to this again and realised there's a better way. It's quite a different approach than what you've got so far, but let me explain...
I know your question title is "How to get Javascript object from HTML element" but my answer better serves the question "How to get HTML element from Javascript object" and also better solves the problem you're facing.
First, I set the stage by creating a container element #paragraph1 and a "Roll Once" button which runs the rollOnce() function
<p id="paragraph1"></p>
<button onclick="rollOnce()">Roll Once</button>
Then I create the Dice() Object which takes a parameter - this parameter is the id of the element we wish to use as a container. We must wait for the HTML to load before we can find that container because until then, it simply doesn't exist yet. That's why I have bound a function to the document.onreadystatechange event.
So when the HTML has loaded and the document is ready, I initialise the Object, storing it in a var and the Object has all the required functions built-in for managing it's button.
function Dice(container) {
this.button = document.createElement("button");
this.button.innerHTML = 0;
document.getElementById(container).appendChild(this.button);
this.button.addEventListener('click', function() {
this.className = 'kept';
});
this.roll = function() {
if(this.button.className != 'kept') {
this.button.innerHTML = Math.floor(Math.random()*6)+1;
}
}
}
var dice1;
var dice2;
var dice3;
document.onreadystatechange = function () {
if(document.readyState == "complete") {
dice1 = new Dice("paragraph1");
dice2 = new Dice("paragraph1");
dice3 = new Dice("paragraph1");
rollOnce();
}
}
function rollOnce() {
dice1.roll();
dice2.roll();
dice3.roll();
}
Fully working demonstration is here: http://codepen.io/anon/pen/groEmg
Edit: If you want to get the values of the dice later, you can access the Objects' properties like so: dice1.button.innerHTML
You need to keep track of what has been kept and what has not been kept. It would be useful to hold all the dice functionality inside the dice class. every time you run rollOnce() you must also represent the kept/unkept state in the className.
Here's an example including what I gather is your current initialisation - define var dice then define rollOnce() then run rollOnce()
function Dice() {
this.value = 0;
this.kept = false;
this.roll = function() {
if(!this.kept) this.value = Math.floor(Math.random()*6)+1;
};
this.keep = function(id) {
this.kept = true;
document.getElementById(id).className = 'kept';
}
}
var dice1 = new Dice();
var dice2 = new Dice();
var dice3 = new Dice();
function rollOnce() {
dice1.roll();
dice2.roll();
dice3.roll();
document.getElementById("paragraph1").innerHTML =
'<button id="diceOne" class="'+(dice1.kept?'kept':'keep')+'" onclick="dice1.keep(\'diceOne\')">'+dice1.value+'</button> ' +
'<button id="diceTwo" class="'+(dice2.kept?'kept':'keep')+'" onclick="dice2.keep(\'diceTwo\')">'+dice2.value+'</button> ' +
'<button id="diceThree" class="'+(dice3.kept?'kept':'keep')+'" onclick="dice3.keep(\'diceThree\')">'+dice3.value+'</button> ';
}
rollOnce();
I've made it pass an ID to Dice.keep(id) just to have a live update of the DOM element which represents this Object variable.
Some clarification on the classnames since you're a beginner: I used ternary logic operators to quickly perform an IF THEN ELSE
So the part that says dice1.kept?'kept':'keep'
Actually means IF dice1.kept THEN 'kept' ELSE 'keep'
You can put a blank '' instead of 'keep' if you like since I don't think it's being used (but you might use it for CSS). Of course, there is plenty of room for improvement all over this code, but I wanted to keep it as similar to your sample code as possible. In fact, the first thing I would do is probably change the onclick to this: onclick="dice1.keep(this)" and then change your object like:
this.keep = function(button) {
this.kept = true;
button.className = 'kept';
}
http://codepen.io/anon/pen/MyrxyX
Edit: here's a slightly modified version where the Dice() object is agnostic to the DOM but still provides all the relevant data: http://codepen.io/anon/pen/MyrxbB
Using edge animate, I don't seem to have the control over orders of operations that I am looking for. I have a function that operates a switch and another that tests the conditions of one or many switches. The problem I am running into is edge keeps wanting to run the test before the switch.
I can have the switch launch the test but run into the issue that I load the objects in an array inside of edge. What I am thinking is I need a way of pre-loading variables that the function can use but they don't need to be global since they are only used in this one function.
Here is what I have so far.
Inside Edge Animate:
twoPhaseSwitch('.btn1','on'); //sets up switch 1
twoPhaseSwitch('.swtch1','off'); //sets up switch 2
conditionsArray(['.btn1','.swtch1']); // tells the test script what the buttons are
In JavaScript file:
function twoPhaseSwitch(object,state)
{
var obj = $(object);
stage.getSymbol(obj).stop(state);
obj.data('state',state);
obj.mousedown(function(e)
{
if(obj.state == 'off')
{
stage.getSymbol(obj).stop('on');
obj.state = 'on';
obj.data('state','on');
}else{
stage.getSymbol(obj).stop('off');
obj.state = 'off';
obj.data('state','off');
};
});
};
function conditionsArray(obj)
{
var answers = ['on','on'];
// compare lengths
if (obj.length != answers.length)
return 'Argument Miscount';
var challengResults = challangeArray();
if (challengResults == true)
{
lightOn('.LED1','on');
}else if(challengResults == false)
{
lightOn('.LED1','off');
};
console.log(challengResults);
function challangeArray()
{
for (var i = 0, l=obj.length; i < l; i++)
{
if ($(obj[i]).data('state') != answers[i]) {
return false;
}
}
return true;
};
};
function lightOn(lightOBJ,state)
{
lightOBJ = $(lightOBJ);
stage.getSymbol(lightOBJ).stop(state);
};
I use mousedown and mouseup currently to fake the order of operations but it brings some pretty unacceptable issues so I am trying to do this right.
did you try wrapping your code in
$( document ).ready(){
your code here
}
to prevent any javascript from running until the page loads?
So, I have this little code in my js file:
window.onload = function Equal() {
var a = 'b1'
var b = 'box1'
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
var a = 'b2'
var b = 'box2'
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
The function itself is not important (it equals checkboxvalues set in the localstorage), but I execute it 2 times. First time with var a & b set to 'b1' & 'box1'. Then I run the script again (same script), but with var a & b set to 'b2' & 'box2'. Now, this code works, but my question is if there is a shorter way to write this? I can imagine some sort of array with a loop, but I could not get it to work for some reason. The 2 variables are pairs, and I know this might be a dumb question, but I can't find the answer anywhere.
You can use a second function which will accept the local storage key and the checkbox id like
window.onload = function Equal() {
setCheckboxState('box1', 'b1');
setCheckboxState('box2', 'b2');
}
function setCheckboxState(id, key) {
document.getElementById(id).checked = 1 == localStorage.getItem(key);
}
You might separate common logic into another function
window.onload = function Equal() {
function extractFromStorage(a, b) {
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
extractFromStorage('b1', 'box1');
extractFromStorage('b2', 'box2');
}
function doTheStuff(a, b) {
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
window.onload = function Equal() {
doTheStuff('b1', 'box1');
doTheStuff('b2', 'box2');
}
?
This is how I would do it.
There are several problems with your code.
You do not check that the element you are stetting an attribute to
exists. You do not check if the localStorage item you get is
defined.
You pollute the global name space with the function name Equal.
That function should not be named with a capital as it is not a Object generator.
There is no need to use setAttribute and removeAttribute, in
fact removeAttribute makes no sense in this case as you can not
remove the checked attribute from the element. BTW why use setAttribute here and not for window.onload?
The checked attribute is either true or false, it does not use the
string "checked"
Binding the load event via the onload attribute is not safe as you may
block 3rd party code, or worse 3rd party code may block you.
There is no error checking. DOM pages are dynamic environments, pages
have adverts and content from many places that can interfer with your
code. Always code with this in mind. Check for possible errors and deal with them in a friendly way for the end user. In this case I used an alert, not friendly for a normal user but for you the coder.
My solution.
// add an event listener rather than replace the event listener
window.addEventListener(
"load", // for the load event
function(){
// the update function that is called for each item;
var update = function(item){
// the right hand side equates to true if the localstorage
// is equal to "1". LocalStorage allways returns a string or
// undefined if the key is not defined.
item.element.checked = localStorage[item.storageName] === "1";
}
// safe element getter
var getElement = function(eId){
var e = document.getElementById(eId); // try and get the element
if(e === null){ // does it exist?
throw "Missing element:"+eId; // no then we can not continue
// the program stops here unless
// you catch the error and deal with
// it gracefully.
}
return e; //ok return the element.
}
// Item creator. This creates a new item.
// sName is the local storage name
// eId id the element ID
var item = function(sName, eId){
return {
storageName: sName, // set the loaclStorage name
element:getElement(eId); // get the element and check its safe
};
}
// make it all safe
try{
// create an array of items.
var items = [
item("b1","box1"),
item("b2","box2")
];
// for each item update the element status
items.forEach(update);
}catch(e){
alert("Could not update page?");
}
}
);
I'm trying to build a function like this one:
var t =$('#top');
var q1=$('#fe1');
var q2=$('#fe2');
var q3=$('#fe3');
var q4=$('#fe4');
var q5=$('#fe5');
var win = $(window);
var doc=$(document);
var wins = win.scrollTop();
var docs = doc.scrollTop();
function next (){
if (wins == docs) {
q1.ScrollTo();
}
else if (wins == q1.scrollTop()) {
q2.ScrollTo();
}
else if (wins == q2.scrollTop()) {
q3.ScrollTo();
}
else if (wins == q3.scrollTop()) {
q4.ScrollTo();
}
else if (wins == q4.scrollTop()) {
q5.ScrollTo();
}
}
I want to go scrolling to the next section. To do so, the code checks in which section I am so it knows which section to scroll to. But I think $(window).scrollTop() is not what I am looking for.
I want a statement that returns the distance between the top of the page and the top of what I am displaying. Maybe i have to do a more complex operation. Do you know how can i get this?
Thanks.
This problem is quite simple to solve even without jQuery or similar in plain JavaScript. Here it is for your example:
var next = (function (sections) {
function getTop(node) {
return node ? node.offsetTop + getTop(node.offsetParent) : 0;
}
return function () {
var i, nodeTop, top = window.pageYOffset;
for (i = 0; i < sections.length; i += 1) {
nodeTop = getTop(document.getElementById(sections[i]));
if (nodeTop > top) {
window.scrollTo(window.pageXOffset, nodeTop);
return;
}
}
};
}(['top', 'fe1', 'fe2', 'fe3', 'fe4', 'fe5']));
The code is quite general, so you can pass any section ids you want (they just need to appear in the correct order).
We use two standard DOM properties/functions here window.pageYOffset and window.scrollTo() to get and set vertical offset of the window (window.pageXOffset is used to keep horizontal offset the same). To get the vertical offset of the section start I defined getTop function using simple recursion (jQuery uses similar code IMHO).
Resulting function next() is defined in a self-invoking closure to hide the implementation and helper function. To use it after this code is run, you simply call
next();
I tested this code, so I am quite confident it works :).
I am working with a decent sized set of data relating to objects on the page and some objects need links applied to them onclick. The link to connect to is part of the dataset and I build a string for the link with the variable linkTarget and apply it like so.
if (dataTag[i][3]==true){
if(prepend==undefined || prepend=="undefined"){
var linkTarget=ResultsJSON["targetUrl"];
ele.onclick = function(){
window.open(linkTarget);
};
} else {
var linkTarget=prepend+ResultsJSON["targetUrl"];
ele.onclick = function(){
window.open(linkTarget);
};
}
ele refers to an element picked up with getElementByID. Now I am going through quite a few objects and the problem I have is the onclick for every object is the last value of linkTarget. This is all contained in a function and link target is a local variable so I have no idea why. I have tried using an array with something like
ele.onclick=function(){window.open(linkTarget[linkTarget.length-1]);};
and even
ele.onclick=function(){window.open(linkTarget.valueOf());};
with the same results. I am at a loss now and would appreciate any help.
Use Array.forEach() to iterate your data and watch your troubles melt away.
dataTag.forEach(function (item) {
if (item[3]==true) {
var linkTarget = "";
if (prepend==undefined || prepend=="undefined") {
linkTarget = prepend;
}
linkTarget += ResultsJSON.targetUrl;
ele.onclick = function () {
window.open(linkTarget);
};
}
});
See this compatibility note for using Array.forEach() in older browsers.
You're in a loop — therefore, you need to put your things-to-be-executed in another function, like so:
if(dataTag[i][3]) {
if(prepend) {
(function(linkTarget) {
ele.onclick = function() {
window.open(linkTarget);
};
})(ResultsJSON.targetUrl);
} else {
(function(linkTarget) {
ele.onclick = function() {
window.open(linkTarget);
};
})(ResultsJSON.targetUrl);
}
I also made some general corrections.