How to explain these lines of JavaScript? - javascript

Both light and order are arrays containing states with the period of time they are displayed.
However I'm not too sure about what Index does or 'sId'. could someone please explain these? The code changes the sequence of a traffic light. The code works however I wanted to know what lightIndex does, as when I change it it doesn't work.
<!DOCTYPE html>
<html>
<style type="text/css">
.light {
height: 30px;
width: 30px;
border-style: solid;
border-width: 2px;
border-radius: 25px;
}
.lightRed {
background-color: red;
}
.lightYellow {
background-color: yellow;
}
.lightGreen {
background-color: green;
}
</style>
<body>
<div id="trafficLight">
<div>Click to Start and Stop</div>
<div class="light" id="Red"></div>
<div class="light" id="Yellow"></div>
<div class="light" id="Green"></div>
</div>
<button type="button" onclick="changeState()">Change Lights</button>
<button type="button" onclick="changeState()">automatic</button>
<script>
var changeState = (function () {
var state = 0,
lights = ["Red", "Yellow", "Green"],
lightsLength = lights.length,
order = [
[7000, "Red"],
[2000, "Red", "Yellow"],
[7000, "Green"],
[2000, "Yellow"]
],
orderLength = order.length,
lightIndex,
orderIndex,
sId;
return function (stop) {
if (stop) {
clearTimeout(sId);
return;
}
var light,
lampDOM;
for (lightIndex = 0; lightIndex < lightsLength; lightIndex += 1) {
light = lights[lightIndex];
lightDOM = document.getElementById(light);
if (order[state].indexOf(light) !== -1) {
lightDOM.classList.add("light" + light);
} else {
lightDOM.classList.remove("light" + light);
}
}
sId = setTimeout(changeState, order[state][0]);
state += 1;
if (state >= orderLength) {
state = 0;
}
};
}());
document.querySelector('change Lights', 'automatic').addEventListener("click", (function () {
var state = false;
return function () {
changeState(state);
state = !state;
};
}()), false);
</script>
</body>
</html>

lightIndex and sId are both just variable names created by whoever wrote the code. They don't 'mean' anything inherently -- the creator of the code could have chosen whatever variable names they wanted. However, in your code, both of these variables have uses that are fairly self-evident from their names.
lightIndex is an integer (between 0 and 2), which is used as an index of the array lights, so that the current light (light) can access the relevant colour with light = lights[lightIndex];. Presumably lights is set up like var lights = ['green', 'orange', 'red']. So, for example, light = lights[lightIndex] becomes light = lights[1], making light become equivalent to orange.
sId is a time-delayed function (setTimeout(changeState, order[state][0]);). setTimeout() takes two parameters -- a function to execute, and a time delay in milliseconds. In this example, order[state][0] will be equivalent to something like 1000 (one second), and changeState() is the function to execute. Essentially, every one second, change the state of the light (to a different colour).
I don't know why you'd try to change lightIndex, but if you want to reverse the direction of the lights changing, you need to reverse the for loop:
for (lightIndex = 0; lightIndex < lightsLength; lightIndex += 1) {
Becomes:
for (lightIndex = lightsLength; lightIndex > 0; lightIndex -= 1) {
If you want to change the speed, you need to change order[state][0]. As you don't show the order variable's code, you'll need to work out how to change that yourself.
Hope this helps!

Related

parameter problem in swap color function (for a button)

For a HTML button:
<button type="button" id="100">100%</button>
I have a java script function, that swaps 2 background colors of that button:
const but1 = document.getElementById("100");
n=0;
but.addEventListener("click", function (){
if (n==0) {
but1.style.backgroundColor = "rgb(225, 165, 0)";
n=1;
} else {
but1.style.backgroundColor = "rgb(225, 165, 0,0)";
n=0;
}
});
Works fine. But i want to create more buttons, and don`t want to copy the same function every time. So to make it more simple, ive written a function that will take parameters , lets say "b" (for buttons),"c1" and "c2" (for colors), and "x" - that represents the "n" value (for every button). So my java script (for the HTML button) with example set of two buttons
const but1 = document.getElementById("button");
n=0;
function colorSwitch (b, c1, c2, x){
if (x==0) {
b.style.backgroundColor = c1;
x=1;
} else {
b.style.backgroundColor = c2;
x=0;
}
}
// orange set
const cOrange = "rgb(225, 165, 0)";
const cOrangeT = "rgb(225, 165, 0,0)";
but1.addEventListener("click", function(){
colorSwitch(but1, cOrange, cOrangeT, n);
});
The problem i have is with "x" parameter. It seems to grab the initial "0" value, then it is set to "1". But when i click the button again the initial value is again "0". So there is no loop and the color doesn't swap.
What am i not understanding in behavior of "x", and how to code it correctly?
To compare an Element computed color towards a color string - see this answer
Toggle styles using CSS class and classList.toggle()
Use a common class (i.e: class="btnTog") as your selector for all your target buttons.
Use Element.classList.toggle() instead, to toggle a .is-active CSS class:
function colorwitch() {
this.classList.toggle('is-active');
}
const btns = document.querySelectorAll(".btnTog");
btns.forEach(btn => btn.addEventListener('click', colorwitch));
.btnTog {
border: 0;
padding: 0.5em;
background-color: rgba(225, 165, 0, 0.5);
}
.is-active {
background-color: rgba(255, 180, 0, 1)
}
<button class="btnTog" type="button">100%</button>
<button class="btnTog" type="button">100%</button>
Toggle colors using data-* attribute to store a state (1/0)
If for some unknown reasons you want to store a state right inside an Element you can use the super handy data-* attribute or dataset property:
function colorSwitch () {
this.style.backgroundColor = [
"rgba(225, 165, 0, 0.5)",
"rgba(255, 180, 0, 1)",
][this.dataset.tog ^= 1]; // Toggle 1, 0, 1, 0, ...
}
const btns = document.querySelectorAll(".btnTog");
btns.forEach(btn => btn.addEventListener('click', colorSwitch));
.btnTog {
border: 0;
padding: 0.5em;
background-color: rgba(225, 165, 0, 0.5);
}
<button class="btnTog" type="button">100%</button>
<button class="btnTog" type="button">100%</button>
NodeList/forEach MDN
Element/dataset MDN
EventTarget/addEventListener MDN
Element/classList MDN
Please refer to the accurate and thorough answer provided by Roko C. Buljan. I was unaware browsers could return a different string from the one stored in an element.
You can do without the parameter altogether. Inside your switching function do a comparison of the current state of the button:
function colorSwitch (b, c1, c2){
if (b.style.backgroundColor === c1) {
b.style.backgroundColor = c2;
} else {
b.style.backgroundColor = c1;
}
}

How can i turn a card into its original position?

I want to know how I can turn back to its original position BOTH cards that didn't match.
If there is a match, there is an alert saying congrats, otherwise try again, but i want that two cards to return to its original position.
The card images are not in here. Can someone help with this?
I want to know how I can turn back to its original position BOTH cards that didn't match.
If there is a match, there is an alert saying congrats, otherwise try again, but i want that two cards to return to its original position.
The card images are not in here. Can someone help with this?
// HOW TO RESTART EVERYTHING WITH TIMEOUT 2s AFTER WINNING OR LOOSING?
//HOW TO BACKFLIP BOTH CARDS IF WRONG?
//GLOBAL VAR
const box = document.getElementsByClassName('box')[0];
var cards = [{
name: 'queen',
clase: 'diamonds',
pic: 'images/queen-of-diamonds.png'
},
{
name: 'queen',
clase: 'hearts',
pic: 'images/queen-of-hearts.png'
},
{
name: 'king',
clase: 'diamonds',
pic: 'images/king-of-diamonds.png'
},
{
name: 'king',
clase: 'hearts',
pic: 'images/king-of-hearts.png'
}
];
function createBoard() {
for (var i = 0; i < cards.length; i++) {
var photo = document.createElement('img');
// console.log(foto);
photo.setAttribute('src', 'images/back.png');
photo.setAttribute('data-id', i);
box.appendChild(photo);
photo.style.paddingLeft = '20px';
photo.style.marginTop = '20px';
photo.style.height = '40%';
photo.style.width = '15%';
photo.style.borderRadius = '3%';
photo.addEventListener('click', function flipcard() {
var cardId = this.getAttribute('data-id');
console.log(cardId + ' is my card id');
var myThis = this; // I want to export THIS cause i cannot access from the other function
cardsInPlay.push(cards[cardId].name);
this.setAttribute('src', cards[cardId].pic);
// console.log(this);
if (cardsInPlay.length === 2) {
checkForMatch(myThis, cardId, photo);
}
});
}
}
createBoard()
var cardsInPlay = [];
//2
function checkForMatch(myThis, cardId, photo) { // match cards dont turn back
if (cardsInPlay[0] === cardsInPlay[1]) {
alert('CONGRATS!!! YOU FOUND A MATCH');
} else {
alert('SORRY TRY AGAIN');
myThis.setAttribute('src', 'images/back.png');
console.log(myThis);
}
}
.box {
background-color: white;
width: 860px;
margin: 50px auto;
/*background-color: black;*/
text-align: center;
background-color: inherit;
}
body {
background-color: #0caab6;
text-align: center;
margin-top: 50px;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<meta charset="utf-8">
<title>CARDS</title>
</head>
<body>
<header>
<h1>PLAY GAME</h1>
</header>
<div class="box">
</div>
<script src="app.js" charset="utf-8"></script>
</body>
</html>
Right now, your cardsInPlay array contains the names of the cards clicked. If you use it to store references to the clicked img elements instead, you'll be able to access everything you need to check for matches and flip the cards back over without passing anything to checkForMatch. Make sure to empty the array after checking!
There's a lot that can be improved here. After you get it working, consider posting to Code Review.

javascript setTimeout function doesn't work inside a loop

I'm making a Simon Game and I'm trying to make the button presses have 1 second interval. But It seems that my setTimeout function is not doing its job and all clicks are performed at once without the 1s interval. I tried alerting something outside the loop and it works just fine. Can anyone help me with this?
This is my JavaScript code:
for (var count = 1; count <= 20; count++) {
$("#count").html(count);
seq.push(Math.floor(Math.random() * 4));
seq.forEach(function(press) {
setTimeout(function() {
eval('$("#button' + press + '").click();');
}, 1000);
});
}
and the corresponding html:
<p>count: <span id="count">0</span></p>
<button id="button0" onclick="sound1.play()"></button>
<button id="button1" onclick="sound2.play()"></button>
<button id="button2" onclick="sound3.play()"></button>
<button id="button3" onclick="sound4.play()"></button>
Thank you!
The problem is the way you do setTimeout.
The for loop iterates within a few milliseconds and you basically request all the clicks to run one second later, so they all happen one second later but at the same time.
If you request the first click after one, the second click after two seconds and so forth, you'll get what you want:
seq.forEach(function(press, i) {
setTimeout(function() {
$("#button" + press).click();
}, 1000 * i);
});
Also note that you probably want to restructure your code to not do this twenty times over:
for (var count = 1; count <= 20; count++) {
$("#count").html(count);
seq.push(Math.floor(Math.random() * 4));
}
seq.forEach(function(press, i) {
setTimeout(function() {
$("#button" + press).click();
}, 1000 * i);
});
Your eval function is running after 1 second but all of them are.
What happens:
loop from 1 to 20
add an item to the seq array
loop through the seq array
define the setTimeout to happen in 1 second.
Your code does not sleep while wating for the setTimeout to execute. So all of them are defined on the loop and happen as near as possible to the 1 second requested.
You could make an asynchronous loop, by calling a function repeatedly from within a setTimeout: that way the sequencing and delay will be as desired.
Here is a working snippet with some other ideas:
// First generate the array (only 8 to not annoy SO public):
var seq = Array.from(Array(8), _ => Math.floor(Math.random() * 4));
function replay() {
// Iterate seq asynchronously
(function loop(i) {
if (i >= seq.length) return; // all done
$("#count").text(i+1);
$("#buttons>button").eq(seq[i]).click();
setTimeout(loop.bind(null, i+1), 1000);
})(0);
}
$("#buttons>button").click(function () {
// Play sound here...
playNote([523, 659, 784, 880][$(this).index()], 800);
// Some visual effect:
$(this).addClass("clicked");
setTimeout($(this).removeClass.bind($(this), "clicked"), 800);
});
// Sound support
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
function playNote(frequency, duration) {
// create Oscillator node
var oscillator = audioCtx.createOscillator();
oscillator.type = 'square';
oscillator.frequency.value = frequency; // value in hertz
oscillator.connect(audioCtx.destination);
oscillator.start();
setTimeout(oscillator.stop.bind(oscillator), duration);
}
// Play the sequence on page load
replay();
button {
border: 2px solid silver;
}
button.clicked {
border: 2px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>count (up to 8): <span id="count">0</span></p>
<div id="buttons">
<button>A</button>
<button>B</button>
<button>C</button>
<button>D</button>
</div>

How can I reference a variable from a js script in html

I've seen similar questions asked before but none of them really helped in my situation. To boil it down I, a noobie, am trying to change an html background color to a variable that was created in a js script and I'm not sure quite how to do this. I want "color" to be used for the background color. h, m, s are hours minutes and seconds. I'm pulling these from the computer and it is working fine printing it. Here is something I found that is nearly identical to what I want to do www.jacopocolo.com/hexclock/
JS
if (h<=9) {h = '0'+h};
if (m<=9) {m = '0'+m};
if (s<=9) {s = '0'+s};
var color = '#'+h+m+s;
$("div.background").css("background-color", color );
HTML
<style>
.background {
width: 100%;
height: 100%;
position: absolute;
vertical-align: middle;
}
</style>
I've tried making a div for the background (shown above) but it doesn't seem to be working. In the end all I'm really set on is this part of it
if (h<=9) {h = '0'+h};
if (m<=9) {m = '0'+m};
if (s<=9) {s = '0'+s};
var color = '#'+h+m+s;
Can someone tell me how to properly do this or even just point me in the general direction? Any help would be greatly appreciated.
I tried it, and it works fine.
$(document).ready(function(){
var h = 0;
var m = 9;
var s = 20;
if (h<=9) {h = '0'+h}
if (m<=9) {m = '0'+m};
if (s<=9) {s = '0'+s};
var color = '#'+h+m+s;
console.log('color' + color);
console.log("Original Color " + $("div.background").css('background-color'));
$("div.background").css("background-color", color );
console.log("Updated Color " + $("div.background").css('background-color'));
});
<html>
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="app.js"></script>
<style>
.background {
width: 100%;
height: 100%;
position: absolute;
vertical-align: middle;
}
</style>
</head>
<body>
<div class="background">hello world</div>
</body>
</html>
The problem in your code is probably that a color parameter pre-pended with '#' must be given in hexadecimal coordinates:
red=10, green=20 and blue=40 should be written like this:
#0A1428
So, you have to convert first to hexadecimal each coordinate like this:
var h = h.toString(16);
var m = m.toString(16);
var s = s.toString(16);
Then, perform the zero left-padding, and so.

Drag and Drop Jquery function not working in site

I'm having a problem getting my code to work when it's incororated into a live site. The fiddle works just fine, but when I include the identical code in a webpage, I can't get the function to load/work at all.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Drag and Drop Assignment</title>
<!doctype html>
<link rel="stylesheet" href="styles/style1.css"/>
<style>
.drop{
float: left;
width: 350px;
height: 400px;
border: 3px solid blue;
background-image: url("http://debsiepalmer.com/images/tardis 2.jpg");
background-repeat: no-repeat;
background-size: cover;
}
#right{float: left;
width: 350px;
height: 400px;
border: 3px solid red;
}
</style>
<script src="draganddrop.js" ></script>
<script src="http://code.jquery.com/jquery-2.0.2.js" ></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js" ></script>
<script type="text/javascript">
$ (init);
function image(id, image1) {
this.id = id;
this.image1 = image1;
}
$('#deal').click(function () {dealAll(
dealCard(randomCard()));
});
$(function() {
$( "#draggable" ).draggable({ containment: "#left"});
});
function init() {
$('.drop').droppable( {
drop: handleDropEvent
} );
$("img").draggable();
}
// global variables
var cardsInDeck = new Array();
var numberOfCardsInDeck = 15;
cardsInDeck[0] = "Ace";
cardsInDeck[1] = "Grace";
cardsInDeck[2] = "Susan";
cardsInDeck[3] = "Ian";
cardsInDeck[4] = "Barbara";
cardsInDeck[5] = "Brigadier";
cardsInDeck[6] = "Romana I";
cardsInDeck[7] = "K9";
cardsInDeck[8] = "Tegan";
cardsInDeck[9] = "Jamie";
cardsInDeck[10] = "Sarah Jane";
cardsInDeck[11] = "Jo";
cardsInDeck[12] = "Romana II";
cardsInDeck[13] = "Yates";
cardsInDeck[14] = "Leela";
var cardsDealt = new Array();
function dealAll(){
var z=0;
for (z=0;z<5;z++) {
cardsDealt[z] = new Image(z,dealCard(randomCard()));
}
}
function dealCard(i) {
if (numberOfCardsInDeck == 0) return false;
var $img = new Image();
$img.src = "images/Companions/" + cardsInDeck[i] + ".jpg";
// Here I set the ID of the object
$img.id=cardsInDeck[i];
$img.class='drag';
document.body.appendChild($img);
$('#'+$img.id).draggable();
removeCard(i);
return $img;
}
// deal randomly - works
function randomCard() {
return Math.floor(Math.random() * numberOfCardsInDeck);
}
function removeCard(c)
{
for (j=c; j <= numberOfCardsInDeck - 2; j++)
{
cardsInDeck[j] = cardsInDeck[j+1];
}
numberOfCardsInDeck--;
numberOfCardsInDeck--;
numberOfCardsInDeck--;
}
function handleDropEvent( event, ui ) {
alert("Fantastic! You chose " + ui.draggable.attr("id") + " to be your companion.");
// Here I want the id of the dropped object
}
</script>
</head>
<body>
<div id="container" div style="width:750px; margin:0 auto;">
<div id="page_content" style="left: 0px; top: 0px; width: 750px" class="auto-style8">
<!--Begin Assignment 10 --->
<div id="left" class="drop">
<img id="tardis" ></img>
</div>
<input type="button" value="Get Companions" id="deal" />
<div id="content" style="left: 0px; top: 0px; width: 750px">
</div>
</div>
</div>
</body>
</html>
It's supposed to generate 5 images, one of which can be selected to be dropped onto the target and generate an alert with the id of the image being dropped. Like I said, it works just fine in the fiddle - and the code is identical on the web page, so I don't understand what I'm doing wrong.
fiddle: http://jsfiddle.net/reaglin/FUvT8/6/
I ordered some code...and for me it worked
// global variables
var cardsInDeck = [],
numberOfCardsInDeck = 5;
cardsInDeck[0] = "Ace";
cardsInDeck[1] = "Grace";
cardsInDeck[2] = "Susan";
cardsInDeck[3] = "Ian";
cardsInDeck[4] = "Barbara";
cardsInDeck[5] = "Brigadier";
cardsInDeck[6] = "Romana I";
cardsInDeck[7] = "K9";
cardsInDeck[8] = "Tegan";
cardsInDeck[9] = "Jamie";
cardsInDeck[10] = "Sarah Jane";
cardsInDeck[11] = "Jo";
cardsInDeck[12] = "Romana II";
cardsInDeck[13] = "Yates";
cardsInDeck[14] = "Leela";
//load "init" when document it's ready
$(document).on('ready',init);
function init() {
$( "#draggable" ).draggable({ containment: "#left"});
$('.drop').droppable( {drop: handleDropEvent});
}
$('#deal').click(function () {
dealAll();
});
$('#reset-pictures').click(function(){
$('img.drag').remove();
numberOfCardsInDeck = 5;
});
// deal 5 cards at once - works
function dealAll(){
// 5 cards max, no repeat cards
while(numberOfCardsInDeck){
var rand = randomCard();
dealCard(rand);
}
}
//deal cards - works
function dealCard(i) {
//create id, remove space id
var id_picture = (cardsInDeck[i] +'-'+i).replace(/\s/g, '');
//validate not exist image
if (!!$('img#'+id_picture).length) {
return;
}
var $img = $('<img/>', { src : "http://debsiepalmer.com/images/companions/" + cardsInDeck[i] + ".jpg", id : id_picture, class : 'drag', 'data-info': cardsInDeck[i]
})
$('body').append($img);
$('img.drag').draggable();
numberOfCardsInDeck--;
}
// deal randomly - works
function randomCard() {
return Math.floor(Math.random() * cardsInDeck.length);
}
// this is what to do when card drops in tardis
function handleDropEvent( event, ui ) {
alert(ui.draggable.attr("data-info"));
}
DEMO
JSFinddle
Are you linking to JQuery correctly in the live version? Sometimes when moving from a development area to a live system the references mess up, you can find out if a reference is working or not by viewing the source code and entering in the link into the URL.
This is how you fix it: Take all the javascript (from $ (init) to the alert()) and place it at the bottom of the loaded page, inside the body, after all the elements. This ensures that the javascript knows what named elements you are talking about.
I would like to emphasise that this is not a tip for writing good javascript. The problem arises because it's bad javascript to begin with. Well written pages do not rely on the position of the code within the page, rather the opposite in fact.
Postscript: I literally did the following: I came here after googling "drag and drop"; I didn't fully read the actual question; I went to the jfiddle; I copied all the code for my own purposes; I couldn't get it to work; I fixed it (just trial and error really); I fixed the bug which is still present even on the jfiddle page; and then I came back here to find out what the original query had been!
The other bug is the fact that you can't drag the images of "Romana I", "Romana II" and "Sarah Jane". As the code makes the array values the id of the image elements, maybe others can instantly spot what causes that problem.

Categories

Resources