I want to make a slot machine. I am taking random index from array and populating it inside my div. But the only issue is that I want to have a slot machine effect. I mean that the effect should be like numbers are dropping from top to bottom. This is my code so far.
var results = [
'PK12345',
'IN32983',
'IH87632',
'LK65858',
'ND82389',
'QE01233'
];
// Get a random symbol class
function getRandomIndex() {
return jQuery.rand(results);
}
(function($) {
$.rand = function(arg) {
if ($.isArray(arg)) {
return arg[$.rand(arg.length)];
} else if (typeof arg === "number") {
return Math.floor(Math.random() * arg);
} else {
return 4; // chosen by fair dice roll
}
};
})(jQuery);
// Listen for "hold"-button clicks
$(document).on("click", ".wheel button", function() {
var button = $(this);
button.toggleClass("active");
button.parent().toggleClass("hold");
button.blur(); // get rid of the focus
});
$(document).on("click", "#spin", function() {
// get a plain array of symbol elements
var symbols = $(".wheel").not(".hold").get();
if (symbols.length === 0) {
alert("All wheels are held; there's nothing to spin");
return; // stop here
}
var button = $(this);
// get rid of the focus, and disable the button
button.prop("disabled", true).blur();
// counter for the number of spins
var spins = 0;
// inner function to do the spinning
function update() {
for (var i = 0, l = symbols.length; i < l; i++) {
$('.wheel').html();
$('.wheel').append('<div style="display: none;" class="new-link" name="link[]"><input type="text" value="' + getRandomIndex() + '" /></div>');
$('.wheel').find(".new-link:last").slideDown("fast");
}
if (++spins < 50) {
// set a new, slightly longer interval for the next update. Makes it seem like the wheels are slowing down
setTimeout(update, 10 + spins * 2);
} else {
// re-enable the button
button.prop("disabled", false);
}
}
// Start spinning
setTimeout(update, 1);
});
// set the wheels to random symbols when the page loads
$(function() {
$(".wheel i").each(function() {
this.className = getRandomIndex(); // not using jQuery for this, since we don't need to
});
});
.wheel {
width: 25%;
float: left;
font-size: 20px;
text-align: center;
}
.wheel .fa {
display: block;
font-size: 4em;
margin-bottom: 0.5em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<div id="wheels">
<div class="wheel clearfix">
</div>
<!-- add more wheels if you want; just remember to update the width in the CSS -->
</div>
<p class="text-center">
<button id="spin" type="button" class="btn btn-default">Spin</button>
</p>
I managed to create a similar effect by using prepend() rather than append(), and adding a set height and hiding the overflow of the wheel.
CSS:
.wheel {
...
height: 34.4px;
overflow: hidden;
}
JS:
$('.wheel').prepend('<div style="display: none;" class="new-link" name="link[]"><input type="text" value="' + getRandomIndex() + '" /></div>');
//Using "first-of-type" rather than "last"
$('.wheel').find(".new-link:first-of-type").slideDown("fast");
See it working here.
Like so many animations it's a lot easier to fake this animation by reversing what appears to be happening, rather than making it work "correctly".
Use the code you have now to generate a result. Then create an animation for a "spinning wheel", you could shuffle divs, or you could make a 3d wheel in css. While the faces are spinning, do some calculations to decide where the wheel should stop to match your results. Then work backwards from there: You'll want to trigger your "stopping" animation so that the face is showing. Your stopping animation would be a predetermined amount of rotation and speed so that a face can be reliably shown. Depending on how fast your wheel spins, the user may lose track, if this is acceptable it may not matter when you trigger as no one could see the wheel jump.
A simulation on the other hand would use a physics model...
Related
I want to find the simplest barebones (that is, no libraries if possible; this is a learning exercise) way to draw a simple line between components. The elements are divs representing cards always stacked vertically potentially forever. Cards can be different heights. The line will exit the left hand side of any given element (card a), turn 90 degrees and go up, turning 90 degrees back into another (card b).
I've tried a few things. I haven't got any fully working yet and they're looking like they all need some serious time dedicated to figuring them out. What I want to know is what's the right/preferred way to do this so that I spend time on the right thing and it's future proof with the view:
I can add as many connecting lines as I need between any two boxes, not just consecutive ones
These lines obey resizing and scrolling down and up the cards
Some cards may not have an end point and will instead terminate top left of page, waiting for their card to scroll into view or be created.
Attempts
My first thought was a <canvas> in a full column component on the left but aligning canvas' and the drawings in them to my divs was a pain, as well as having an infinite scrolling canvas. Couldn't make it work.
Next I tried <div>s. Like McBrackets has done here. Colouring the top, bottom and outer edge of the div and aligning it with the two cards in question but while I can position it relative to card a, I can't figure out how to then stop it at card b.
Lastly I tried <SVG>s. Just .getElementById() then add an SVG path that follows the instructions above. i.e.
const connectingPath =
"M " + aRect.left + " " + aRect.top + " " +
"H " + (aRect.left - 50) +
"V " + (bRect.top) +
"H " + (bRect.left);
Nothing seems to line up, it's proving pretty difficult to debug and it's looking like a much more complex solution as I need to take into account resizing and whatnot.
You might be able to apply something like this by taking a few measurements from the boxes you want to connect; offsetTop and clientHeight.
Update Added some logic for undrawn cards requirement.
While this doesn't fully simulate dynamic populating of cards, I made an update to show how to handle a scenario where only one card is drawn.
Click connect using the default values (1 and 5). This will show an open connector starting from box 1.
Click "Add box 5". This will add the missing box and update the connector.
The remaining work here is to create an event listener on scroll to check the list of connectors. From there you can check if both boxes appear or not in the DOM (see checkConnectors function). If they appear, then pass values to addConnector which will connect them fully.
class Container {
constructor(element) {
this.connectors = new Map();
this.element = element;
}
addConnector(topBox, bottomBox, displayHalf = false) {
if (!topBox && !bottomBox) throw new Error("Invalid params");
const connector = new Connector(topBox, bottomBox, displayHalf);
const connectorId = `${topBox.id}:${bottomBox.id}`;
this.element.appendChild(connector.element);
if (this.connectors.has(connectorId)) {
connector.element.style.borderColor = this.connectors.get(connectorId).element.style.borderColor;
} else {
connector.element.style.borderColor = "#" + Math.floor(Math.random() * 16777215).toString(16);
}
this.connectors.set(connectorId, connector);
}
checkConnectors() {
this.connectors.forEach((connector) => {
if (connector.displayHalf) {
connector.firstBox.updateElement();
connector.secondBox.updateElement();
if (connector.firstBox.element && connector.secondBox.element) {
this.addConnector(connector.firstBox, connector.secondBox);
}
}
});
}
}
class Box {
constructor(id) {
this.id = id;
this.updateElement();
}
getMidpoint() {
return this.element.offsetTop + this.element.clientHeight / 2;
}
updateElement() {
this.element ??= document.getElementById(`box${this.id}`);
}
static sortTopDown(firstBox, secondBox) {
return [firstBox, secondBox].sort((a,b) => a.element.offsetTop - b.element.offsetTop);
}
}
class Connector {
constructor(firstBox, secondBox, displayHalf) {
this.firstBox = firstBox;
this.secondBox = secondBox;
this.displayHalf = displayHalf;
const firstBoxHeight = this.firstBox.getMidpoint();
this.element = document.createElement("div");
this.element.classList.add("connector");
this.element.style.top = firstBoxHeight + "px";
let secondBoxHeight;
if (this.displayHalf) {
secondBoxHeight = this.firstBox.element.parentElement.clientHeight;
this.element.style.borderBottom = "unset";
} else {
secondBoxHeight = this.secondBox.getMidpoint();
}
this.element.style.height = Math.abs(secondBoxHeight - firstBoxHeight) + "px";
}
}
const connectButton = document.getElementById("connect");
const error = document.getElementById("error");
const addBoxButton = document.getElementById("addBox");
const container = new Container(document.getElementById("container"));
connectButton.addEventListener("click", () => {
const firstBoxId = document.getElementById("selectFirstBox").value;
const secondBoxId = document.getElementById("selectSecondBox").value;
if (firstBoxId === "" || secondBoxId === "") return;
error.style.display = firstBoxId === secondBoxId ? "block" : "none";
const firstBox = new Box(firstBoxId);
const secondBox = new Box(secondBoxId);
// Check for undrawn cards
if (!!firstBox.element ^ !!secondBox.element) {
return container.addConnector(firstBox, secondBox, true);
}
const [topBox, bottomBox] = Box.sortTopDown(firstBox, secondBox);
container.addConnector(topBox, bottomBox);
});
window.addEventListener("resize", () => container.checkConnectors());
addBoxButton.addEventListener("click", () => {
const box = document.createElement("div");
box.innerText = 5;
box.id = "box5";
box.classList.add("box");
container.element.appendChild(box);
addBoxButton.style.display = 'none';
container.checkConnectors();
});
.box {
border: solid 1px;
width: 60px;
margin-left: 30px;
margin-bottom: 5px;
text-align: center;
}
#inputs {
margin-top: 20px;
}
#inputs input {
width: 150px;
}
.connector {
position: absolute;
border-top: solid 1px;
border-left: solid 1px;
border-bottom: solid 1px;
width: 29px;
}
#error {
display: none;
color: red;
}
<div id="container">
<div id="box1" class="box">1</div>
<div id="box2" class="box">2</div>
<div id="box3" class="box">3</div>
<div id="box4" class="box">4</div>
</div>
<div id="inputs">
<input id="selectFirstBox" type="number" placeholder="Provide first box id" min="1" value="1" max="5" />
<input id="selectSecondBox" type="number" placeholder="Provide second box id" min="1" value="5" max="5" />
<div id="error">Please select different boxes to connect.</div>
</div>
<button id="connect">Connect</button>
<button id="addBox">Add box 5</button>
I am absolutely a noob when it comes to Javascript so I hope someone can help me please. I made a very simple Vanilla JS + HTML code that counts the number of times that it reaches 10 seconds (10 seconds = 1 count). This code will also refresh the page onmouseleave and when I change tab using window.onblur. My problem is that every time the page refreshes, the counter will go back to zero. What I want is that for the counter to deduct just one (or a specific number of) count every page refresh instead of completely restarting the count to zero. Please help me with Vanilla Javascript only and no JQuery (because I am planning to use this code personally and offline). Thank you in advance.
For those who may wonder what's this code is for, I want to create this to encourage myself to stay away from my computer for a certain period everyday. Like, if I can stay away from my computer for 100 counts, then I can use my computer freely after. I am addicted to the internet and I want to make this as my own personal way of building self-control.
Here is my code:
<style>
label {
color: orange;
}
p {
border-radius: 0px;
color: black;
text-decoration: none;
font-family: Consolas !important;
font-size: 16px;
font-weight: normal;
outline: none;
line-height: 0.25 ;
}
</style>
<body onmouseleave="window.location.reload(true)">
<p>You have earned <label id="pointscounter">00</label> point/s.</p>
<script>
var PointsLabel = document.getElementById("pointscounter");
var totalCountPoints = 0;
setInterval(setTimePoints, 10000);
function setTimePoints() {
++totalCountPoints;
PointsLabel.innerHTML = pad(totalCountPoints);
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
</script>
<script>
var blurred = false;
window.onblur = function() { blurred = true; };
window.onfocus = function() { blurred && (location.reload()); };
</script>
</body>
Storage
If you want the data to survive a reload, you need to save it somewhere. There are multiple options you can use. I used localStorage. You can learn more about it here: https://www.w3schools.com/jsref/prop_win_localstorage.asp. localStorage even survives closing the Browser Tab.
If you want to reset the data in a new session, you can use sessionStorage (just replace localStorage with sessionStorage): https://www.w3schools.com/jsref/prop_win_sessionstorage.asp.
What I did:
Save data on blur
If a blur-event occurs, the data is saved.
I also stopped the interval because there is no need for the interval anymore.
blurred variable
There is (currently?) no need for this variable.
The only usecase seems to be:
window.onfocus = function() {
blurred && location.reload();
};
To my knowledge you don't need this variable here.
Comming back
If the user already has points in localstorage, the current Points are calculated based on the points in localstorage. It currently deducts 1 point.
Using onmouseleave
I replaced the location.reload(true) on the body-tag with a function call. Everytime the mouse leaves, it calls this function. This function calls the onBlur function. The onBlur function is there, to ensure, that both window.onblur and onmouseleave do the same thing (save & stop). After the onBlur function is called, an EventListener is added to wait for mouseenter. When the mouse is seen again, we can reload the page with the onFocus function. It wouldn't reload the page as soon as the mouse left, because the timer would start (bc of reload), even if the mouse wasn't on the document.
Todo:
There is currently no check to see, if a the mouse in on the document after a reload. The timer will begin, even if the mouse isn't on the document.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
label {
color: orange;
}
p {
border-radius: 0px;
color: black;
text-decoration: none;
font-family: Consolas !important;
font-size: 16px;
font-weight: normal;
outline: none;
line-height: 0.25;
}
</style>
</head>
<body onmouseleave="mouseLeft()">
<p>You have earned <label id="pointscounter">00</label> point/s.</p>
<script>
var PointsLabel = document.getElementById("pointscounter");
var totalCountPoints = 0;
// Calculate Points if user has already collected points
if (localStorage.getItem("points") !== null) {
// You can change, how many points to deduct
const pointsToDeduct = 1;
var tempPoints = localStorage.getItem("points");
totalCountPoints = tempPoints - pointsToDeduct;
// Reset to 0 if points now negative
if (totalCountPoints < 0) {
totalCountPoints = 0;
}
PointsLabel.innerHTML = pad(totalCountPoints);
}
// need to save, to stop/clear it later
var timePointsInterval = setInterval(setTimePoints, 10000);
function setTimePoints() {
++totalCountPoints;
PointsLabel.innerHTML = pad(totalCountPoints);
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
function mouseLeft() {
onBlur();
document.addEventListener("mouseenter", onFocus);
}
function onBlur() {
// save Current Points:
localStorage.setItem("points", totalCountPoints);
//stop the timer
clearInterval(timePointsInterval);
}
function onFocus() {
location.reload();
}
// Blur Detection
var blurred = false;
window.onblur = function () {
// [-] blurred = true;
onBlur();
};
window.onfocus = function () {
// [-] blurred && location.reload();
onFocus();
};
</script>
</body>
</html>
I am using simulation for mouse click operation using element.click() in js file, but mouse cursor is some where else and action is performing on correct element, i want to have mouse cursor on element while performing the simulate mouse click.Does anyone know using javascript code, how can i get this?
You cannot move the mousepointer with javascript, because of the security implications that it incurs.
As already pointed out in other answers, you can't change the real mouse position with JavaScript... but... do you need to do that? No!
You can add a image of a mouse cursor and place that on any position you want on top of your content, relative to the browser window top-left.
You can hide the real cursor by applying css 'cursor: none;' class to the zone of the screen you want the cursor to disappear.
So to simulate what you want you can get the position of the element you want to click, hide real mouse cursor, show fake mouse cursor and move that one to the position of the element you want to click, then click it.
To make it user friendly: please notify the user to not move his mouse anymore when you start the simulation, simulate mouse move and click, when user moves his mouse hide the fake mouse and show real mouse and notify user that simulation is over.
You can't change mouse cursor postion in browser. see this.
But you can use javascript click() method to simulate click event. To do this work you must use elementFromPoint() to select click position. In my bottom example when you click on first button, javascript simulate second button click.
var first = document.getElementById("first");
var second = document.getElementById("second");
first.addEventListener("click", function(){
var xPos = second.offsetLeft;
var yPos = second.offsetHeight;
document.elementFromPoint(xPos, yPos).click();
});
second.addEventListener("click", function(){
alert("Second button clicked!");
});
<button id="first">First</button>
<button id="second">Second</button>
I found a git-repository that simulates a mouse-drag event:
link to git-repository
SEE CODEPEN EXAMPLE HERE
useful article
HTML
<!--
author: kemokid
github: https://github.com/kemokid/scripting-sortable/blob/master/script_sortable_dnd_more_general.js
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="main.css">
</head>
<body>
<!-- https://ghostinspector.com/blog/simulate-drag-and-drop-javascript-casperjs/ -->
<p>Drag the W3Schools image into the rectangle:</p>
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<br>
<img id="drag1" src="https://www.w3schools.com/html/img_logo.gif" ondragstart="drag(event)" >
<br/>
<button onClick="autodrag();">Auto-drag</button>
<br/>
<br/>
Reload the page to reset the image.
<script src="app.js"></script>
</body>
</html>
CSS
/*
author: kemokid
github: https://github.com/kemokid/scripting-sortable/blob/master/script_sortable_dnd_more_general.js
*/
#div1 {
width: 350px;
height: 70px;
padding: 10px;
border: 1px solid #aaaaaa;
}
#drag1 {
width: 336px;
height: 69px;
}
JS
/*
author: kemokid
github: https://github.com/kemokid/scripting-sortable/blob/master/script_sortable_dnd_more_general.js
*/
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
var id = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(id));
}
function autodrag() {
return triggerDragAndDrop('#drag1', '#div1');
}
// Originally from https://ghostinspector.com/blog/simulate-drag-and-drop-javascript-casperjs/
// trimmed down and modified by #kemokid (Martin Baker) to work with Sortable.js
///
// Probably not going to work with dragging from one list to another
// This has been made more general, to work with other drag-and-drop elements besides a
// Sortable list, but is not as complete as the Casper example above.
// Call with DOM selectors, eg `triggerDragAndDrop('#drag', '#drop');`
// Returns false if unable to start.
// callback, if present, will be called with true if able to finish, false if not.
// If you receive "false" on the callback, the list is likely not in the beginning state.
var triggerDragAndDrop = function (selectorDrag, selectorDrop, callback) {
var DELAY_INTERVAL_MS = 10;
var MAX_TRIES = 10;
var dragStartEvent;
// fetch target elements
var elemDrag = document.querySelector(selectorDrag);
var elemDrop = document.querySelector(selectorDrop);
if (!elemDrag || !elemDrop) {
console.log("can't get elements");
return false;
}
var startingDropRect = elemDrop.getBoundingClientRect();
function rectsEqual(r1, r2) {
return r1.top === r2.top && r1.right === r2.right && r1.bottom === r2.bottom && r1.left === r2.left;
}
// function for triggering mouse events
function fireMouseEvent(type, elem, dataTransfer) {
var evt = document.createEvent('MouseEvents');
evt.initMouseEvent(type, true, true, window, 1, 1, 1, 0, 0, false, false, false, false, 0, elem);
if (/^dr/i.test(type)) {
evt.dataTransfer = dataTransfer || createNewDataTransfer();
}
elem.dispatchEvent(evt);
return evt;
};
function createNewDataTransfer() {
var data = {};
return {
clearData: function(key) {
if (key === undefined) {
data = {};
} else {
delete data[key];
}
},
getData: function(key) {
return data[key];
},
setData: function(key, value) {
data[key] = value;
},
setDragImage: function() {},
dropEffect: 'none',
files: [],
items: [],
types: [],
// also effectAllowed
}
};
// trigger dragging process on top of drop target
// We sometimes need to do this multiple times due to the vagaries of
// how Sortable manages the list re-arrangement
var counter = 0;
function dragover() {
counter++;
console.log('DRAGOVER #' + counter);
var currentDropRect = elemDrop.getBoundingClientRect();
if (rectsEqual(startingDropRect, currentDropRect) && counter < MAX_TRIES) {
if (counter != 1) console.log("drop target rect hasn't changed, trying again");
// mouseover / mouseout etc events not necessary
// dragenter / dragleave events not necessary either
fireMouseEvent('dragover', elemDrop, dragStartEvent.dataTransfer);
setTimeout(dragover, DELAY_INTERVAL_MS);
} else {
if (rectsEqual(startingDropRect, currentDropRect)) {
console.log("wasn't able to budge drop target after " + MAX_TRIES + " tries, aborting");
fireMouseEvent('drop', elemDrop, dragStartEvent.dataTransfer);
if (callback) callback(false);
} else {
setTimeout(drop, DELAY_INTERVAL_MS);
}
}
}
function drop() {
console.log('DROP');
// release dragged element on top of drop target
fireMouseEvent('drop', elemDrop, dragStartEvent.dataTransfer);
fireMouseEvent('mouseup', elemDrop); // not strictly necessary but I like the symmetry
if (callback) callback(true);
}
// start dragging process
console.log('DRAGSTART');
fireMouseEvent('mousedown', elemDrag);
dragStartEvent = fireMouseEvent('dragstart', elemDrag);
// after a delay, do the first dragover; this will run up to MAX_TRIES times
// (with a delay between each run) and finally run drop() with a delay:
setTimeout(dragover, DELAY_INTERVAL_MS);
return true;
};
I'm trying to create a simple slideshow effect. I have 10 images, and I've created a basic HTML page with 2 buttons to go to the right or left image. On clicking the button, the images change.
Now, I'm trying to add a basic fade functionality to the changing image. But the fade effect isn't getting displayed. When I put alerts, I notice that the fade is taking place, but without the alerts it is too fast to be visible. Also, it is happening on the previous image, instead of the next one.
<html>
<head>
<style>
.main {
text-align: center;
}
.centered {
display: inline-block;
}
#image {
border: solid 2px;
width: 500px;
height: 500px;
}
#number {
font-size: 30px;
}
</style>
<script>
function goLeft() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==1) {
image.src = "Images\\10.jpg"
pos.innerHTML = 10;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)-1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)-1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
function changeOpacity(image, i) {
alert(parseFloat(i*4/100).toString());
image.style.opacity = (parseFloat(i*4/100).toString()).toString();
}
function goRight() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==10) {
image.src = "Images\\1.jpg"
pos.innerHTML = 1;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)+1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)+1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
</script>
</head>
<body>
<div class="main">
<div class="centered">
<img id="image" src="Images\1.jpg">
</div>
</div>
<div class="main">
<div class="centered">
<span id="number">1</span>
</div>
</div>
<div class="main">
<div class="centered">
<button onclick="goLeft()" style="margin-right:50px;">Go Left</button>
<button onclick="goRight()" style="margin-left:50px;">Go Right</button>
</div>
</div>
</body>
The problem is this block of code that is in your goLeft method, and goRight method:
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You are creating 25 timers that, and each timer will execute approximately 1 second later.
Creating animations is best left to the CSS.
In your CSS add:
#image {
transition: opacity 0.5s ease;
}
And then in your JavaScript, simply: image.style.opacity = 1.0;
When the opacity changes, CSS will automatically transition the opacity length at the speed defined in the css, e.g 0.5s. Feel free to experiment.
I also added a jsfiddle here: http://jsfiddle.net/dya7L8wq/
You misunderstood setTimeout and the for loop.
Norman's answer provides a good solution with CSS, but he doesn't talk too much about why your code is not working. So I'd like to explain.
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You assumption is:
invoke changeOpacity(image, 0) after 1 second
invoke changeOpacity(image, 1) 1 second after step 1
invoke changeOpacity(image, 2) 1 second after step 2
invoke changeOpacity(image, 3) 1 second after step 3
....
And the last step is invoking changeOpacity(image, 24) 1 second after previous step.
What actually happens is:
The loop is finished almost immediately!
In each iteration, setTimeout queues an asynchronous function invocation, and it's done! That says, it will return right away, rather than wait until changeOpacity returns.
And then, after about 1 second, changeOpacity fires 25 times almost at the same time, because you queued it 25 times in the for loop.
Another problem here is: in changeOpacity invocations, passed-in parameter i are not 1, 2, 3...., they all have the same value that causes for loop to exit (1 second ago) - 25, because JS doesn't have a block scope prior to ES6 (in ES6 we have keyword let for it).
In a pure JS solution, to ensure the time sequence we'd usually queue next invocation at the end of every step:
function changeOpacity() {
// do something here
// before the function returns, set up a future invocation
setTimeout(changeOpacity, 1000)
}
Here's an example to print a list of numbers from 1 to 5:
var go = document.getElementById('go')
var op = document.getElementById('output')
var i = 0
function printNum() {
var p = document.createElement('p')
p.innerHTML = ++i
op.appendChild(p)
// next step
if(i < 5) {
setTimeout(printNum, 500)
}
}
go.onclick = printNum
<button id="go">GO</button>
<div id="output"></div>
Why use pure JavaScript?
Use jQuery.
It has a pretty neat fadeTo() function and a useful fadeIn() function.
Might wanna use that ;)
That is the best way I could describe it. Basically, I have a jpg that I have scrolling across the screen onLoad. I should be able to stop the image, and make it hidden after it has made 3 descending passes across the screen. I have been at it for a couple hours now, but can't figure out how to make it work without the use of an onClick button. I need it to stop on its own. Here is the script:
function moveit()
{
dom=document.getElementById("roman").style;
dom.top= parseInt(dom.top)+tinc+"px";
dom.left= startleft+"px";
dom.visibility= "visible";
startleft=startleft+linc;
if (startleft<= 20)
{linc=linc*-1;
window.document.roman.src="roman.jpg"; }
if (startleft>= window.screen.width-10)
{linc=linc*-1;
window.document.roman.src="roman.jpg"; }
to=setTimeout("moveit();", 100) ;
}
And the body looks like this:
<body onload="moveit()">
<div id="roman" style="position:absolute; top: 0px; left: 0px; visibility: hidden;">
<img name="roman" src="roman.jpg"/>
</div>
<form>
<div id="button" style="position:absolute; top: 315px; left: 10px;">
<input type="button" value="stop" onClick="clearTimeout(to)">
</div>
</form>
I have tried several ways, but have not been able to remove the button aspect. I would like the image to makes 3 full passes from right to left, stop, and then become hidden. Any thoughts oh how to do it without the button?
You want to use a counter variable to keep track of how many times your image bounces horizontally. Once that counter reaches a certain number, don't reset the timeout:
var passes = 0;
var imageStyle = document.getElementById("roman").style;
imageStyle.visibility = "visible";
// Referring to the image with document.roman isn't recommended; give it an ID
// and use getElementById instead.
window.document.roman.src = "roman.jpg";
function moveit() {
imageStyle.top = parseInt(dom.top) + tinc + "px";
imageStyle.left = startleft + "px";
startleft = startleft + linc;
if (startleft <= 20 || startleft >= window.screen.width - 10) {
linc = linc * -1;
passes++;
}
if (passes < 3) {
to = setTimeout(moveit, 100);
}
}
Notice that I removed some redundant code. You don't have to set the visibility or image source every time the timer fires. Just do it at the beginning.