onmousedown/onmouseup function not working - javascript

I have a rectangle I am trying to move using javascript. I can get it to move left and right using functions, but when I hold the mouse down it doesn't continue to move. Here is my html code:
<button onmousedown="moveLeft();" onmouseup="stopMove();">LEFT</button>
<button onmousedown="moveRight();"
onmouseup="stopMove();">RIGHT</button><br><br>
Here is my javascript code (the conditional statements are just so the rectangle doesn't move off of the canvas):
function moveLeft () {
xx-=20;
if (xx<0) {
xx=0;
}
}
function moveRight() {
xx+=20;
if (xx>400) {
xx=400;
}
}
function stopMove () {
xx+=0;
}

When you press a mouse button, the mousedown event will fire just once. It does not continuously fire while you have the button held down. What you'll need to do is use a timer to begin the movement and then, on mouseup, stop the timer.
Also, don't use inline HTML event handlers (onmouseover, onmouseout, etc.). Here's why.
Lastly, since your moveLeft and moveRight functions are essentially the same thing, they can be combined into one function that simply takes in an argument that determines the direction.
Here's a working example:
// Get references to the HTML elements you'll need to interact with:
var btnLeft = document.getElementById("btnLeft");
var btnRight = document.getElementById("btnRight");
var box = document.getElementById("box");
// Set up event handlers for the HTML elements in JavaScript, using modern
// standards, not in the HTML. Both the left and right buttons need to
// call the same function, but with a different argument value. This is
// why the events are being bound to another function that wraps the actual
// call to the move function (so that an argument can be passed).
btnLeft.addEventListener("mousedown", function(){ move(-20); });
btnRight.addEventListener("mousedown", function(){ move(20); });
btnLeft.addEventListener("mouseup", stopMove);
btnRight.addEventListener("mouseup", stopMove);
// The counter needs to be accessible between calls to move
var xx = 0;
// We'll start an automatic timer, but we'll need to stop it later
// so we need a variable set up for that.
var timer = null;
// Only one function is needed to address the move operation
// The argument is what determines the direction
function move(amount) {
// Stop any previous timers so that we don't get a runaway effect
clearTimeout(timer);
xx += amount;
// Check the new position to see if it is off the visible page
if (xx < 0){
xx = window.innerWidth;
} else if(xx > window.innerWidth){
xx = 0;
}
// Just displaying the current value
box.textContent = xx;
// Move the box to the new location
box.style.left = xx + "px";
// Run the timer after a 250 millisecond delay and have
// it call this function again. Assign the variable to
// the timer.
timer = setTimeout(function() { move(amount) }, 250);
}
function stopMove () {
xx += 0;
// Stop the timer
clearTimeout(timer);
}
#box {
background-color:blue;
border:2px solid black;
color:yellow;
text-align:center;
font-weight:bold;
padding-top:.5em;
margin-top:3em;
/* Everything above is just for display and not actually needed.
But in order to move the box and to be able to have a consistent
size for it, the following is required: */
position:absolute;
height:50px;
width:50px;
}
<button id="btnLeft">LEFT</button>
<button id="btnRight">RIGHT</button>
<div id="box"></div>

Since the mouse evet does not fire while it is down, you need to fire the event manually until you detect the other event. So to do this, you need to either use setInterval or setTimeout
var moveTimer,
xx = 200;
function move (dir) {
xx+=20*dir;
if (xx<0) {
xx=0;
} else if (xx>400) {
xx=400
}
stopMove()
moveTimer = window.setTimeout(move.bind(this,dir), 100)
document.getElementById("out").value = xx;
}
function stopMove () {
if(moveTimer) window.clearTimeout(moveTimer);
moveTimer = null
}
window.addEventListener("mouseup", stopMove)
<button onmousedown="move(-1);">LEFT</button>
<button onmousedown="move(1);">RIGHT</button>
<input type="number" id="out"/>

Related

if/else and setInterval

<script>
//when user clicks start button this function ensures all fields //are set to 0 and it
//sets the timer for the game (90seconds) and the second timer to //call showWord() every four seconds to display a new word
function startGame() {
numBadWordsField.innerHTML = '';
numGoodWordsField.innerHTML = '';
numWordsRight = 0;
numWordsWrong = 0;
correctWords = [];
showWord();
gameTimer = setInterval(gameTime, 1000);
timedWordDisplay = setInterval(showWord, 4000);
}
//this function is set to repeat every four seconds unless the user //types the word
//correctly in which case code in the checkWord() function resets setInterval then and a new word appears
function showWord() {
let randomNum = Math.floor(Math.random()*wordsLevelOne.length);
currentWord = wordsLevelOne[randomNum];
//i put all correctly typed words in an array to avoid them being repeated
//if the random word has been typed correctly and is in the array then i tell the
//program to repeat the function until a new word is found.
if (correctWords.includes(currentWord)) {
showWord();
} else {
wordDisplayBox.innerHTML = currentWord;
setInterval(changeBar, 500);
answerBox.focus();
}
}
//this function is called oninput as user types in the word. it works perfectly (i think it does anyways)
//however i cannot figure out how to give instructions in the event the user does not type the
//word correctly before the four seconds are up and the setInterval repeats. I would like to
//in that case increment the words wrong score and reset the fields to be ready for the next
//word to be displayed
function checkWord() {
let currentWordLen = answerBox.value.length;
if (wordDisplayBox.innerHTML === answerBox.value) {
clearInterval(timedWordDisplay);
numWordsRight++;
correctWords.push(currentWord);
numGoodWordsField.innerHTML = numWordsRight;
answerBox.value = '';
answerBox.focus();
wordDisplayBox.innerHTML = '';
showWord();
timedWordDisplay = setInterval(showWord, 4000);
} else if (answerBox.value === currentWord.substring(0, currentWordLen)) {
answerBox.style.borderColor = 'green';
} else {
answerBox.style.borderColor = 'red';
}
}
//different topic than above but i also researched how to make my progress bar fill slowly over the course
//of the four seconds. i have written the following function identically to that on
//w3schools and other code yet it does not work.
//Any ideas?
function changeBar() {
let proBar = document.querySelector('#progressBar');
var width = 1;
var id = setInterval(frame, 10);
function frame() {
if (width >= 100) {
clearInterval(id);
} else {
width++;
proBar.style.width = width + '%';
}
}
}
</script>
This project Im working on is a beginner level speed typing game that displays a different word for the user to type in less than four seconds.I have a setInterval that displays a different word every four seconds unless the user types the word correctly at which point the timer starts over then. What I am stumped at is how can I make it so that if the correct answer is not typed in before the interval resets (at the end of four seconds) the program knows to increment the 'wrong answer' score and to reset the input boxes for the next word just like when it is typed correctly. i have attached the parts of my code i think may be relevant. If anyone has any suggestions let me know. I am eager to learn. **I am not familiar yet with JQuery. Please describe any suggestions using vanilla JS
This feature should be implemented in the showWord function.
showWord is executed after 4 seconds have passed, which is when the time is up. Executing this function means the user has failed to type the word in time.
I would do something like this :
function showWord() {
// At this point, the user has lost. We perform the according operations
numWordsWrong++;
answerBox.value = '';
// etc.
// What follows is the rest of the function you've already implemented
let randomNum = Math.floor(Math.random()*wordsLevelOne.length);
// etc.
}
To answer your question about the progress bar, you are setting an interval to run changeBar every 500 milliseconds, which would cause the progress bar to reset every half second. If you want a delay before starting the progress bar use setTimeout.
In addition, you are running your progress bar to move 1% every 10 milliseconds which would result in the bar completing in 1 second. If you want the bar to complete in 4 seconds, set the id interval to run every 40 milliseconds.
Without seeing your css and html, I have to assume you're using the correct id names in your code but if nothing is happening at all, that could also be the cause.
I have looked at the W3Shools code you reference and I tried to replicate what you were trying to do and got this to work:
<html>
<head>
<style>
#myProgress {
width: 100%;
background-color: #ddd;
}
#myBar {
width: 1%;
height: 30px;
background-color: #4CAF50;
}
</style>
</head>
<body>
<div id="myProgress">
<div id="myBar"></div>
</div>
</body>
<script>
function changeBar() {
let proBar = document.querySelector('#myBar');
var width = 1;
var id = setInterval(frame, 40);
function frame() {
if (width >= 100) {
clearInterval(id);
} else {
width++;
proBar.style.width = width + '%';
}
}
}
setTimeout(changeBar, 100);
</script>
</html>
One solution can be to create a new function (ex : showWordBecauseTimeout) and call it in your setInterval instead of showWord. And call that function in showWord fct instead of in startGame fct.
So the new code would be something like :
function showWord() {
clearInterval(timedWordDisplay);
timedWordDisplay = setInterval(showWordBecauseTimeout, 4000);
// you also need to move the cleaning of the input in the showWord fct
// ...
}
function showWordBecauseTimeout() {
numWordsWrong++;
showWord()
}
Hope that it helps you :).

Detect new mouse wheel event

I'm using the following event listener to detect mouse wheel and scroll direction:
window.addEventListener('wheel', ({ deltaY }) => {
console.log(deltaY);
if (deltaY > 0) scrollDown();
else if (deltaY < 0) scrollUp();
});
The following happens here:
2 finger touch pad scroll on Macbook triggers the event handler
deltaY keeps logging due to the scroll accelerometer
scrollDown() or scrollUp() keep firing until accelerometer stops
I only want to fire scrollUp and scrollDown once per user interaction. I therefore need to detect a new mouse scroll event, not every mouse scroll events. Is this possible?
I did try a timeout to detect if deltaY was still changing due to the accelerometer, but this wasn't sufficient because if it was still changing, a second user interaction did not trigger scrollUp or scrollDown.
Here's a CodePen of what I'm trying to achieve: https://codepen.io/anon/pen/dQmPNN
It's very close to the required functionality, but if you hammer the mouse wheel hard on the first slide, then try to scroll to the next one immediately, the timeout solution locks it so you have to wait another second or so until the timeout completes and you can continue scrolling.
This is old but I found it when looking for an answer to pretty much the same problem.
I solved the problem for my purposes, so here's my solution in case it helps anyone else.
The problem is really to define what counts as one continuous action. Without something more concrete to work with, it's just a question of timing. It's the time between events that's the key - so the algorithm is to keep accumulating events until there's a certain gap between them. All that then remains is to figure out how big the allowed gap should be, which is solution specific. That's then the maximum delay after the user stops scrolling until they get feedback. My optimum is a quarter of a second, I'm using that as a default in the below.
Below is my JavaScript, I'm attaching the event to a div with the id 'wheelTestDiv' using jQuery but it works the same with the window object, as in the question.
It's worth noting that the below looks for any onWheel event but only tracks the Y axis. If you need more axes, or specifically only want to count events towards the timer when there's a change in deltaY, you'll need to change the code appropriately.
Also worth noting, if you don't need the flexibility of tracking events against different DOM objects, you could refactor the class to have static methods and properties, so there would be no need to create a global object variable. If you do need to track against different DOM objects (I do), then you may need multiple instances of the class.
"use strict";
class MouseWheelAggregater {
// Pass in the callback function and optionally, the maximum allowed pause
constructor(func, maxPause) {
this.maxAllowedPause = (maxPause) ? maxPause : 250; // millis
this.last = Date.now();
this.cummulativeDeltaY = 0;
this.timer;
this.eventFunction = func;
}
set maxPause(pauseTime) {
this.maxAllowedPause = pauseTime;
}
eventIn(e) {
var elapsed = Date.now() - this.last;
this.last = Date.now();
if ((this.cummulativeDeltaY === 0) || (elapsed < this.maxAllowedPause)) {
// Either a new action, or continuing a previous action with little
// time since the last movement
this.cummulativeDeltaY += e.originalEvent.deltaY;
if (this.timer !== undefined) clearTimeout(this.timer);
this.timer = setTimeout(this.fireAggregateEvent.bind(this),
this.maxAllowedPause);
} else {
// just in case some long-running process makes things happen out of
// order
this.fireAggregateEvent();
}
}
fireAggregateEvent() {
// Clean up and pass the delta to the callback
if (this.timer !== undefined) clearTimeout(this.timer);
var newDeltaY = this.cummulativeDeltaY;
this.cummulativeDeltaY = 0;
this.timer = undefined;
// Use a local variable during the call, so that class properties can
// be reset before the call. In case there's an error.
this.eventFunction(newDeltaY);
}
}
// Create a new MouseWheelAggregater object and pass in the callback function,
// to call each time a continuous action is complete.
// In this case, just log the net movement to the console.
var mwa = new MouseWheelAggregater((deltaY) => {
console.log(deltaY);
});
// Each time a mouse wheel event is fired, pass it into the class.
$(function () {
$("#wheelTestDiv").on('wheel', (e) => mwa.eventIn(e));
});
Web page ...
<!DOCTYPE html>
<html>
<head>
<title>Mouse over test</title>
<script src="/mouseWheelEventManager.js"></script>
</head>
<body>
<div id="wheelTestDiv" style="margin: 50px;">Wheel over here</div>
</body>
</html>
Have you tried breaking this out into functions with a flag to check if an interaction has occurred?
For example:
// Create a global variable which will keep track of userInteraction
let shouldScroll = true;
// add the event listener, and call the function when triggered
window.addEventListener('wheel', () => myFunction());
//Create a trigger function, checking if shouldScroll is true or false.
myFunction(){
shouldScroll ? (
if (deltaY > 0) scrollDown();
else if (deltaY < 0) scrollUp();
// Change back to false to prevent further scrolling.
shouldScroll = false;
) : return;
}
/* call this function when user interaction occurs
and you want to allow scrolling function again.. */
userInteraction(){
// set to true to allow scrolling
shouldScroll = true;
}
We can avoid such a situation by delay execution and removing the events in between the delay, refer below example and have added 1000ms as delay which can be modified based on your requirements.
let scrollPage = (deltaY)=>{
console.log(deltaY);
if (deltaY > 0) scrollDown();
else if (deltaY < 0) scrollUp();
};
var delayReg;
window.addEventListener('wheel', ({ deltaY }) => {
clearTimeout(delayReg);
delayReg = setTimeout(scrollPage.bind(deltaY),1000);
});

How do I make this fade in/out infinite?

this code fades in and fades out the div #shape while the start variable is true.
when i call the "start" method from event "click" the browser stops working because the while inside method is infinitive and "click" event does not finish until "start" method is done.
i want the method to run after the "click" event is finished.
what should i do?
CSS
#shape {
background-color:red;
width:100px;
height:100px;
display:none;
}
HTML
<div id="shape"></div>
<button id="startButton">start game!</button>
JS
var start = false;
$("#startButon").click(function () {
start = true;
startGame();
});
function startGame() {
while (start == true) {
$("#shape").fadeIn(1000).delay(1000).fadeOut(1000);
}
}
You don't need the flag, just make a recursive function. I changed the time to 300 milliseconds so you can see it easier
http://jsfiddle.net/zfbptz9c/
$("#startButton").click(function () {
startGame();
});
function startGame() {
$("#shape").fadeIn(300, function () {
$("#shape").fadeOut(300, function () {
startGame();
});
});
}
The div will fade in and on complete of the fade in, it will fade out then call the startGame function again and the entire process will repeat infinitely.
Alternatively, this can be achieved with css only if you only need to target modern browsers. I will put this fiddle link here, it is from a different question. I won't paste the code since you did not tag the question with css but the fiddle shows everything. I take no credit for it.
How can I create a looping fade-in/out image effect using CSS 3 transitions?
http://jsfiddle.net/FTLJA/261/
JavaScript runs in a single threaded environment, meaning once you enter an infinite loop, you can only quit the loop from within it. In synchronous execution, like the one you have here, no code outside the loop can affect the loop condition.
As far as your problem, people suggested solutions such as making a recursive function or using CSS3 transitions.
Another possible way could be to use timing functions like setTimeout and/or setInterval
The code bellow will make the fadeIn/Out happen after every second, after start button is clicked and until stop button is clicked.
var toggle = true; // flag for animation direction
var shape = $("#shape"); // so we don't select the shape each time we animate
var duration = 1000; // animation duration
var delay = 1000; // delay between animations
var timerId; // timer id returned by setInterval
// start animating the shape after the delay
$("#startButton").click(function() {
timerId = setInterval(animate, delay);
});
// stop animating the shape and hide it
$("#stopButton").click(function() {
clearInterval(timerId);
shape.css('display', 'none');
});
// function that animates the shape depending on the toggle flag
function animate() {
if (toggle) {
shape.fadeIn(duration);
toggle = false;
} else {
shape.fadeOut(duration);
toggle = true;
}
}
#shape {
background-color: red;
width: 100px;
height: 100px;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="shape"></div>
<button id="startButton">start game!</button>
<button id="stopButton">stop game!</button>

Javascript fade in doesn't visibly animate

So I've created the following function to fade elements in and passed in a div that I want to fade in which in this case is an image gallery popup that I want to show when a user clicks an image thumbnail on my site. I'm also passing in a speed value (iSpeed) which the timeout uses for it's time value. In this case I'm using 25 (25ms).
I've stepped through this function whilst doing so it appears to be functioning as expected. If the current opacity is less than 1, then it is incremented and it will recall itself after the timeout until the opacity reaches 1. When it reaches one it stops fading and returns.
So after stepping through it, I take off my breakpoints and try to see it in action but for some reason my gallery instantly appears without any sense of fading.
var Effects = new function () {
this.Fading = false;
this.FadeIn = function (oElement, iSpeed) {
//set opacity to zero if we haven't started fading yet.
if (this.Fading == false) {
oElement.style.opacity = 0;
}
//if we've reached or passed max opacity, stop fading
if (oElement.style.opacity >= 1) {
oElement.style.opacity = 1;
this.Fading = false;
return;
}
//otherwise, fade
else {
this.Fading = true;
var iCurrentOpacity = parseFloat(oElement.style.opacity);
oElement.style.opacity = iCurrentOpacity + 0.1;
setTimeout(Effects.FadeIn(oElement, iSpeed), iSpeed);
}
}
}
Here's where I'm setting up the gallery.
this.Show = function (sPage, iImagesToDisplay, oSelectedImage) {
//create and show overlay
var oOverlay = document.createElement('div');
oOverlay.id = 'divOverlay';
document.body.appendChild(oOverlay);
//create and show gallery box
var oGallery = document.createElement('div');
oGallery.id = 'divGallery';
oGallery.style.opacity = 0;
document.body.appendChild(oGallery);
//set position of gallery box
oGallery.style.top = (window.innerHeight / 2) - (oGallery.clientHeight / 2) + 'px';
oGallery.style.left = (window.innerWidth / 2) - (oGallery.clientWidth / 2) + 'px';
//call content function
ImageGallery.CreateContent(oGallery, sPage, iImagesToDisplay, oSelectedImage);
//fade in gallery
Effects.FadeIn(oGallery, 25);
}
Could anyone help me out?
Also, I'm using IE10 and I've also tried Chrome, same result.
Thanks,
Andy
This line:
setTimeout(Effects.FadeIn(oElement, iSpeed), iSpeed);
calls Effects.FadeIn with the given arguments, and feeds its return value into setTimeout. This is exactly like foo(bar()), which calls bar immediately, and then feeds its return value into foo.
Since your FadeIn function doesn't return a function, that would be the problem.
Perhaps you meant:
setTimeout(function() {
Effects.FadeIn(oElement, iSpeed);
}, iSpeed);
...although you'd be better off creating that function once and reusing it.
For instance, I think this does what you're looking for, but without recreating functions on each loop:
var Effects = new function () {
this.FadeIn = function (oElement, iSpeed) {
var fading = false;
var timer = setInterval(function() {
//set opacity to zero if we haven't started fading yet.
if (fading == false) { // Consider `if (!this.Fading)`
oElement.style.opacity = 0;
}
//if we've reached or passed max opacity, stop fading
if (oElement.style.opacity >= 1) {
oElement.style.opacity = 1;
clearInterval(timer);
}
//otherwise, fade
else {
fading = true;
var iCurrentOpacity = parseFloat(oElement.style.opacity);
oElement.style.opacity = iCurrentOpacity + 0.1;
}
}, iSpeed);
};
};
Your code has a lot of problems. The one culpable for the element appearing immediately is that you call setTimeout not with a function but with the result of a function, because Effects.FadeIn will be executed immediately.
setTimeout(function(){Effects.FadeIn(oElement, iSpeed)}, iSpeed);
will probably act as you intend.
But seriously, you probably should not re-invent this wheel. jQuery will allow you to fade elements in and out easily and CSS transitions allow you to achieve element fading with as much as adding or removing a CSS class.
T.J. and MoMolog are both right about the bug: you're invoking the Effects.FadeIn function immediately before passing the result to setTimeout—which means that Effects.FadeIn calls itself synchronously again and again until the condition oElement.style.opacity >= 1 is reached.
As you may or may not know, many UI updates that all take place within one turn of the event loop will be batched together on the next repaint (or something like that) so you won't see any sort of transition.
This jsFiddle includes the suggested JS solution, as well as an alternate approach that I think you may find to be better: simply adding a CSS class with the transition property. This will result in a smoother animation. Note that if you go this route, though, you may need to also include some vendor prefixes.

How to make a Div's height change slower

I want to make a Div appear by scrolling down on a button click slowly from the top of the page. But when I do it with what I have now, it just appears very fast and does not really slide down. What am I doing wrong?
function showstuff(inquiryForm){
document.getElementById(inquiryForm).style.visibility="visible";
for (var i=0;i<300;i++)
{
document.getElementById(inquiryForm).style.height= i + "px";
}
}
You are looping 300 items and you try to find the element with getELementById and then trying to style the selected item
I think that makes the process really slow and laggy.
Here is an example that should help you understand the event loop,
and the use of setTimeout.
<div id="myDiv" style="width:10px;height:50px;background:#f00;"></div>
<button class="btn" onclick="start();">Start</button>
<button class="btn" onclick="stop();">Stop</button>
<button class="btn" onclick="reset();">Reset<button>
var timeout;
function start() {
var div = document.getElementById("myDiv");
var size = 10;
var func = function () {
timeout = setTimeout(func, 0);
div.style.width = size + "px";
if (size++ == 600) stop();
}
func(); // starts the process
}
function stop() {
clearInterval(timeout);
}
function reset() {
var div = document.getElementById("myDiv");
div.style.width = "10px";
}
this sort of thing is often handled using jquery, as it includes various animation functions, including a convenient short form that would be useful here:
once you've included the jquery library, you can use a function like this:
$(inquiryForm).slideDown( 500 );
where the argument is duration of the effect in ms
like so:
function showstuff(inquiryForm){
$(inquiryForm).slideDown( 500 );
}

Categories

Resources