How do I implement press and hold button javascript? - javascript

I'm a complete novice, looking for instructions on implementing javascript. I am attempting to replace a YUI slider with buttons and a text field. I am trying to achieve buttons that, when held down, will continue to make the text field increase, preferably at a faster and faster rate. (http://www.blackbird502.com/white.htm)I have this in the java tag in the head:
function holdit(btn, action, start, speedup) {
var t;
var repeat = function () {
action();
t = setTimeout(repeat, start);
start = start / speedup;
}
btn.mousedown = function() {
repeat();
}
btn.mouseup = function () {
clearTimeout(t);
}
/* to use */
holdit(btn, function () { }, 1000, 2);
/* x..1000ms..x..500ms..x..250ms..x */
I have no clue how to implement the press and hold into the following in the body:
<form><input type=button value="UP" class="btn" onClick="javascript:this.form.amount.value++;"><br /><input type=text name=amount value=5 class="text"><br /> <input type=button value="DOWN" class="btn" onClick="javascript:this.form.amount.value--;" ></form>
Is it possible? Thanks.

This code should do everything you're looking for; it's based very loosely on tj111's example. I tried to make it as reusable as possible, and it doesn't need JavaScript mixed in with the HTML.
You do need to add IDs to the buttons (btnUP and btnDOWN) and text field (amount). You can change these IDs in the window.onload statement.
// This function creates a closure and puts a mousedown handler on the element specified in the "button" parameter.
function makeButtonIncrement(button, action, target, initialDelay, multiplier){
var holdTimer, changeValue, timerIsRunning = false, delay = initialDelay;
changeValue = function(){
if(action == "add" && target.value < 1000)
target.value++;
else if(action == "subtract" && target.value > 0)
target.value--;
holdTimer = setTimeout(changeValue, delay);
if(delay > 20) delay = delay * multiplier;
if(!timerIsRunning){
// When the function is first called, it puts an onmouseup handler on the whole document
// that stops the process when the mouse is released. This is important if the user moves
// the cursor off of the button.
document.onmouseup = function(){
clearTimeout(holdTimer);
document.onmouseup = null;
timerIsRunning = false;
delay = initialDelay;
}
timerIsRunning = true;
}
}
button.onmousedown = changeValue;
}
//should only be called after the window/DOM has been loaded
window.onload = function() {
makeButtonIncrement(document.getElementById('btnUP'), "add", document.getElementById('amount'), 500, 0.7);
makeButtonIncrement(document.getElementById('btnDOWN'), "subtract", document.getElementById('amount'), 500, 0.7);
}

This is kind of quick and dirty, but it should give you a start. Basically you want to set up a few initial "constants" that you can play with to get the desired behavior. The initial time between increments is 1000 ms, and on each iteration if become 90% of that (1000, 990, 891, ... 100) and stops getting smaller at 100 ms. You can tweak this factor to get faster or slower acceleration. The rest I believe is pretty close to what I think you were going for. It seems like you were just missing the event assignments. In the window.onload you'll see that i assign the onmouseup, and onmousedown events to functions that just call the increment() or decrement() functions with your initial timeout, or the ClearTimeout() function to stop the counter.
EDIT: I changed this slightly to fix the bug. Now if you move your mouse pointer off the button and release it will stop the counter.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title><!-- Insert your title here --></title>
<script>
// Fake Constants
var INITIAL_TIME = 1000;
var ACCELERATION = .9;
var MIN_TIME = 100;
// create global variables to hold DOM objects, and timer
var up = null,
down = null,
count = null,
timer = null;
// Increment the counter
function increment (time) {
// decrease timeout by our acceleration factor, unless it's at the minimum
time = (time * ACCELERATION > MIN_TIME) ? (time * ACCELERATION) : MIN_TIME;
count.value ++ ;
// set the timeout for the next round, and pass in the new smaller timeout
timer = setTimeout(
function () {
increment(time);
}, time);
}
// Same as increment only subtracts one instead of adding.
// -- could easily make one function and pass an pos/neg factor instead
function decrement (time) {
time = time * ACCELERATION > MIN_TIME ? (time * ACCELERATION) : MIN_TIME;
count.value --;
timer = setTimeout(
function () {
decrement(time);
}, time);
}
// Initialize the page after all the forms load
window.onload = function () {
// initialization function
// assign DOM objects to our vars for ease of use.
up = document.getElementById('up_btn');
down = document.getElementById('dwn_btn');
count = document.getElementById('count');
// create event handlers for mouse up and down
up.onmousedown = function () {
increment(INITIAL_TIME);
}
down.onmousedown = function () {
decrement(INITIAL_TIME);
}
document.onmouseup = function () {
clearTimeout(timer);
}
}
</script>
</head>
<body>
<!-- Insert your content here -->
<form name="the_form">
<input type="button" value="Up" id="up_btn" /><br />
<input type="button" value="Down" id="dwn_btn" /></br>
<br />
Count:
<input type="text" value="0" id="count" />
</form>
</body>
</html>

The easiest method would be to just add an ID to each of the buttons, then use those to retrieve the elements and add the events.
//should only be called after the window/DOM has been loaded
window.onload = function() {
//the buttons
var btnUP = document.getElementById('btnUP');
var btnDOWN = document.getElementById('btnDOWN');
//the amount
var amount = document.getElementById('amount');
//actions to occur onclick
var upClick = function() {
amount.value++;
}
var downClick = function() {
amount.value--;
}
//assign the actions here
holdit(btnUP, upClick, 1000, 2);
holdit(btnDOWN, downClick, 1000, 2);
}
<form>
<input type=button value="UP" class="btn" id='btnUP'>
<br />
<input type=text name=amount value=5 class="text" id='amount'>
<br />
<input type=button value="DOWN" class="btn" id='btnDOWN'>
</form>

One aspect not to be overlooked is that you're hooking into the onclick event - which happens on a complete click (Mouse key down and key up). It sounds like you would want to listen for another distinct event, http://www.w3schools.com/jsref/jsref_onmousedown.asp'>onMouseDown . I think if you were to then implement some of the other timer based solutions, already given you would get the functionality you're asking for.
Good luck!

Related

Music player time slider is "laggy"

I'm new here so maybe my question will be a bit difficult to understand.
So here's the problem:
I have built a website for myself in html, JavaScript, and css which includes a music player.
It's not ready yet cause I have many problems with it. But let's focus on the main problem.
So like every other music player application, mine also has a time slider. The main function would be to display the current song's current time and to make me able to skip it wherever I want. I guess you know what I mean despite my english level.
The skip part is working just fine. Not perfect but okay for now.
But the slider's movement is weird. It starts okay in the first few seconds, but after it beginst to take more and more time to jump to the next point.
This is the skipping part's code:
let slider = document.getElementById("durationSlide");
function changeDuration(){
//console.log("currentTime: " + currentSong.currentTime);
sliderPosition = currentSong.duration * (slider.value / 100);
currentSong.currentTime = sliderPosition;
}
And this would be the part where I was supposed to just follow the little dot along the slide 'till the end, but it stops after a while:
function rangeSlider(){
let position = 0;
if(!isNaN(currentSong.duration)){
position = currentSong.currentTime * (100 / currentSong.duration);
slider.value = position;
}
if(currentSong.ended){
currentSong.currentTime = 0;
currentCounter += 1;
position = 0;
PlayFunction();
}
}
UPDATE:
my code is pretty messy...
The function where it is called in every second. (I hope)
function PlayFunction() {
timer = setInterval(rangeSlider, 1000);
playIcon.style.display = "none";
pauseIcon.style.display = "inline";
songList[currentCounter].classList.add("current");
//console.log(songList[currentCounter].classList);
titles.innerHTML = songList[currentCounter].title;
songCounter.innerHTML = currentCounter+1 + "/" + document.getElementsByTagName("audio").length;
checkCurrent();
}
And the html for the slider:
<div class="duration">
<input class="durSlider" id="durationSlide" type="range" min="0"
max="100" value="0"
onchange="changeDuration()">
</div>
testimg
from 0 to 10-13 seconds it goes fine but after it starts to skip time.
And every time I click on pause it jumps to the position where it supposed to be.
Your code appears to work fine as I could not duplicate the problem. The lag you see is probably related to how your code interfaces with the audio player and the browser UI. If you are using a timer loop to update the UI, then one change that might help is switching to event based updates. As shown in the code snippet below, the audio player has a number of events you could use to update the position and duration. For example, this event handler would allow your audio player to update the UI with the position changes. If you haven't read it already, MDN has a very nice walkthrough on using the Web Audio API
currentSong.addEventListener("timeupdate", function(e) {
if (!busy) {
position.value = currentSong.currentTime;
positionText.innerHTML = toTimeString(currentSong.currentTime);
}
});
// Original Code
let slider = document.getElementById("durationSlide");
function changeDuration() {
sliderPosition = currentSong.duration * (slider.value / 100);
currentSong.currentTime = sliderPosition;
}
// And this would be the part where I was supposed to just follow the little dot along the slide 'till the end, but it stops after a while:
function rangeSlider() {
let position = 0;
if (!isNaN(currentSong.duration)) {
position = currentSong.currentTime * (100 / currentSong.duration);
slider.value = position;
}
if (currentSong.ended) {
currentSong.currentTime = 0;
currentCounter += 1;
position = 0;
PlayFunction();
}
}
// Code used for testing
var busy = false;
// fired when media properties like duration are known
currentSong.addEventListener("loadedmetadata", function() {
position.max = currentSong.duration;
});
// do not update value when user is trying to change it
currentSong.addEventListener("timeupdate", function(e) {
if (!busy) {
position.value = currentSong.currentTime;
positionText.innerHTML = toTimeString(currentSong.currentTime);
}
});
currentSong.addEventListener("ended", function() {
position.value = 0;
});
// user is setting position
position.addEventListener("input", function() {
busy = true;
positionText.innerHTML = toTimeString(position.value);
});
play.addEventListener("click", function() {
if (currentSong.paused) {
currentSong.play();
play.classList.add("active");
} else {
currentSong.pause();
play.classList.remove("active");
}
});
position.addEventListener("change", function() {
busy = false;
currentSong.currentTime = position.value;
})
// volume 0.0 to 1.0
volume.addEventListener("input", function() {
currentSong.volume = volume.value;
volumeText.innerHTML = (100 * volume.value).toFixed() + "%";
});
function toTimeString(sec) {
return new Date(sec * 1000).toISOString().slice(11, -5);
}
#play:after {
font-weight: bold;
font-size: 1.2em;
content: ">";
/* play symbol */
}
#play.active:after {
content: "II";
/* pause symbol */
}
<h3>Demo</h3>
<p>Click the play button and use the position and volume sliders.</p>
<audio id="currentSong" src="https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3"></audio>
<div style="padding:2px;margin:5px;">
<button id="play"></button>
</div>
<div>
<div>Position:</div>
<input type="range" value="0" id="position" />
<span id="positionText"></span>
</div>
<div>
<div>Volume:</div>
<input type="range" min="0" max="1" step="0.01" id="volume" />
<span id="volumeText"></span>
</div>

How do I make a countdown feature in a button in HTML5

I am creating a clicker-like game in HTML5+Javascript and for the clicking part, there is a timeout... I tried it my self but for some reason it does not work.
HTML:
function pursuit() {
var btn = document.getElementById("pursuit");
setInterval(function(){
timeOnPursuit -= 1;
}, 1000)
while(timeOnPursuit > 0){
btn.value = "In Pursuit: " + timeOnPursuit;
}
}
<div class="button">
<button id="pursuit" class="pursuit" onClick="pursuit()"> Go on a Pursuit </button>
</div>
A few things to consider here...
First, the code is producing an error in the browser console. You never defined the timeOnPursuit variable, so you aren't really controlling its scope. Simply declare that variable with an initial value (whatever value you like) before trying to use it:
var timeOnPursuit = 10;
Aside from that, your loop is going to block the thread. Because the variable is initially greater than 0, and because nothing in that loop modifies that value, it's going to run forever and never let the interval get a chance to modify the value.
Get rid of the loop entirely and rely on the interval instead. Within the interval you can update the UI. (Which, incidentally, should probably be innerText instead of value in this case.) Then within that interval you can also check when to stop the interval, which is what I suspect you were trying to do with the loop.
For a bonus, you might also want to disable the button so the user can't click it again, which would create another counter.
Overall, maybe something like this:
function pursuit() {
var btn = document.getElementById("pursuit");
// Start the "pursuit"
var timeOnPursuit = 10;
btn.innerText = "In Pursuit: " + timeOnPursuit;
btn.disabled = true;
// Repeat every second
var interval = setInterval(function(){
// Update the "pursuit"
timeOnPursuit -= 1;
btn.innerText = "In Pursuit: " + timeOnPursuit;
// Stop the interval at 0
if (timeOnPursuit <= 0) {
clearInterval(interval);
}
}, 1000)
}
<div class="button">
<button id="pursuit" class="pursuit" onClick="pursuit()"> Go on a Pursuit </button>
</div>
function pursuit() {
let timeOnPursuit = 10 // define time
var btn = document.getElementById("pursuit");
const timer = setInterval(function() {
console.log(timeOnPursuit)
if (timeOnPursuit > 0) {
btn.innerHTML = "In Pursuit: " + timeOnPursuit;
timeOnPursuit -= 1; // reduce time
} else {
btn.innerHTML = "Pursuit";
clearInterval(timer) // stop the timer
}
}, 1000)
}
<div class="button">
<button id="pursuit" class="pursuit" onClick="pursuit()"> Go on a Pursuit </button>
</div>
I was going to write a lot of what David wrote so it seems a little redundant now. Instead here's an alternative approach that uses setTimeout instead of setInterval.
// Cache the element and add a click listener to it
// (no need for inline JS)
const btn = document.querySelector(".pursuit");
btn.addEventListener('click', pursuit, false);
function pursuit() {
// Initialise timeOnPursuit
function loop(timeOnPursuit = 10) {
// Display the button text, and disable the button
btn.innerText = `In Pursuit: ${timeOnPursuit}`;
btn.disabled = true;
// If timeOnPursuit is greater than zero
// call loop again with a a decremented timeOnPursuit
if (timeOnPursuit > 0) {
setTimeout(loop, 1000, --timeOnPursuit);
}
}
// Call the loop function
loop();
}
<div class="button">
<button class="pursuit"> Go on a Pursuit </button>
</div>

jquery, while loop running in the background, simultaneous while loops

1) How make that while loop would run in background, and the webpage would respond to user clicks despite while loop.
If I start the characters generating while loop, I am not able to input the data to the "input", because the generating loop occupies all resources. Actually, whenever I click "start", I am getting the message that the webpage is not responding asking if I want to stop it. After choosing "to stop", I see that the characters are generated. Nevertheless, it is very difficult to input the characters to the input field and to stop the program with "stop" trigger, and usually webpage crashes.
2) How to make several jquery while loops to run simultaneously, and additionally, webpage should be responsive and accessible to user.
<!DOCTYPE html>
<html>
<head>
<script src="theme/assets/js/jquery/jquery-2.2.3.min.js"></script>
</head>
<body>
<div id="Start"> <b> Start </b> </div>
<div id="Stop"> <b> Stop </b> </div>
<br><br> <div id="random"> </div>
<br><br> <input id="input" type="text" size="500">
<script>
// how to manage without global variables? how to pass variables to the function
var flag = false;
var charstr = "zxcvbnm,\.\/asdfghjkl;\'qwertyuiop[]\\`ąčęėįšųū90\-ž";
var charstrL = charstr.length;
$("#Start").click( function() {
$("#lesson").text("clicked Start");
flag =true;
$(this).val('flag');
while(flag){
setInterval(function() { // this code is executed every 500 milliseconds:
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}, 500);
}//while
}); // $("#Start").click( function() {
$("#Stop").click( function(){
flag=false;
$(this).val('flag');
alert('clicked Stop');
});
</script>
</body>
</html>
You can't make a while loop run in the background if it's doing DOM manipulation, because there's only one main UI thread in browser JavaScript.
You also probably don't want to, because this code:
while (flag) {
setInterval(function() { // this code is executed every 500 milliseconds:
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}, 500);
}
continuously adds additional timers to call that code every 500ms. In a very short period of time, your browser will become completely non-responsive.
Just set up setInterval, and have the code inside decide whether to run based on flag:
setInterval(function() { // this code is executed every 500 milliseconds:
if (flag) {
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}
}, 500);
You can have several of those, though if you have a lot of them you might consider having fewer and just having them do more than one thing each time.
This is a working example for a program prompting user to input characters utilizing setInterval function instead of while loop. The application is to improve the typing skills.
<!DOCTYPE html>
<html>
<head>
<script src="theme/assets/js/jquery/jquery-2.2.3.min.js"> </script>
</head>
<body>
<div id="Start"> <b> Start </b> </div>
<br><div id="lesson"> </div>
<input id="input" type="text" size="500">
<span id="Stop"> Stop </span>
<span id="Clear">
Clear </span>
<script>
// how to manage without global variables ? how to pass vaiables to the function
var flag = false;
var charstr = "zxcvbnm,\.\/asdfghjkl;\'qwertyuiop[]\\`ąčęėįšųū90\-ž";
var charstrL = charstr.length;
$("#Start").click( function() {
flag =true;
$(this).val('flag');
if(flag){
setInterval( function() { // this code is executed every 500 milliseconds:
if (flag) {
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text("\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0"+charstr[num]);
} //if (flag) {
}, 500);
} // if (flag)
}); // $("#Start").click( function() {
/*
// does not work, because creates infinite loops, each with 500ms waiting time.
// In miliseconds this accumulates to hours and thousands of loops generated usign while
while(flag){
setInterval(function() { // this code is executed every 500 milliseconds:
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}, 500);
}//while */
//
$("#Stop").click( function(){
flag=false;
$(this).val('flag');
});
$("#Clear").click( function(){
$("#input").val('');
$("#lesson").text(' ');
});
</script>
</body>
</html>

Check is class exist longer than X in javascript

I wrote simple monitor which check few things on my networks and display status - I am fetching statuses using ajax.
When something is wrong I am adding class error to div which is displaying current status.
Later I add player with error sound when class error is present it plays music. I set 3 second interval and thats all.
But not always error mean error, sometimes I recive false warning.
Now I am looking for way to play sound when class error exists longer than XX seconds.
I suppose I have to wrote function with interval 1s, add every hit 1 to temp variable and check is variable bigger than else clean temp variable, but maybe is there more elegant way.
i guess it should work
$('.error').each(function(){
var e = $(this)
setTimeout(function(){
if (e.attr('class') == 'error'){
e.attr('class','error-with-sound');
}
},2000);
});
You should take two vars store the current time at their respective events.
var oldTime, newTime;
// Now when you are adding the class
$(something).addClass("someclass");
oldTime = new Date().getTime(); // Store the timestamp.
//And when you are removing the class
$(something).removeClass("someclass");
newTime = new Date().getTime(); // Store the timestamp.
// Now you check whether class was added for more then XX Seconds
var _diff = (newTime - oldTime)/1000; // Diference is in seconds.
There is no direct way for that, you can add timestamps in your class with separator something like error_1448953716457 and later you can split that and can compare with current timestamps
$('#na').click(function () {
var t = new Date().getTime();
$('h1').addClass("error_" + t);
});
$('#nr').click(function () {
var t = new Date().getTime();
$("[class^=error]").each(function (e) {
$("span").html("Diff. Seconds : " + ((t - ($(this).attr('class').split(' ').pop().split('_')[1])) / 1000).toString());
});
});
input {
width:100px;
}
.error {
color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<h1>.addClass()</h1>
<input id="na" value="Add Class1" type="button" />
<br/>
<input id="nr" value="Calculate Diff" type="button" />
<span></span>
If you want to track .error elements on the page, set an independent interval that looks for those elements and tracks the ones it has seen before by setting an attribute or data value in jquery.
Remember to clearInterval(interval) if you no longer need to check for .error elements.
(function ($) {
// set constants in milliseconds as desired
var INTERVAL_LENGTH = 100, // how often to check DOM for error elements
NOTIFY_AFTER = 3000; // handle error after this length
// check for .error elements and handle those that have been around for a while
var interval = setInterval(function () {
var now = Date.now();
$(".error").each(function () {
var t = $(this).data('error-time');
if(t) {
if(now - t > NOTIFY_AFTER) {
handleErrorElement(this);
}
}
else {
$(this).data('error-time', now);
}
});
}, INTERVAL_LENGTH);
function handleErrorElement(elem) {
// do what you need for error elements visible past a threshold
console.log("handling error element: ", elem);
}
})(jQuery);

object method not modifying other property in the same object

I'm trying to set up a function that sets up a countdown and then triggers another function/event when it reaches 0. I'm linking it with HTML to set up various button selectors to be able to increment the countdown by 1, 2, or 3 ticks.
The problem is that I can get the page to display the min/max timer with no issue, by adding document.getElementById("timer").innerHTML = myTime.curTime + "/" + myTime.maxTime
at the top of the HTML document, but I can't get it to update the curTime, and curTime never seems to update inside the object either. Is this just an issue with how I've structured it? Or should I be approaching this a different way?
Here is what I have so far:
(link to the jsfiddle: http://jsfiddle.net/g4Lu3n43/)
//set the initial value of timer
var timerDown = 0
//set up the countdown constructor
function Countdown(name, baseTime, inc) {
this.name = name;
this.timer = baseTime;
this.increment = inc;
this.maxTime = maxTime();
this.curTime = curTime();
}
//set up the functions for the Countdown prototype
Countdown.prototype = {
maxTime: function() {
return (this.timer + this.increment) * 2;
},
curTime: function() {
return maxTime - timerDown;
}
}
//set up the function to now de-increment the timer to 0
function deIncrement(numInc) {
timerDown += numInc;
if (myTime.curTime <= 0 ) {
alert("You've run out of time!");
} else {
updateTimer();
}
}
//set up the countdown object
var myTime = new Countdown("Timer", 10, 2);
//set up the function to update the div on the HTML page
function updateTimer() {
var timer = document.getElementById("timer")
timer.innerHTML = myTime.curTime + "/" + myTime.maxTime;
}
window.onload = updateTimer();
And the HTML portion of it:
<div id="timer">Time will go here</div>
<input type="button" id="inc1" onclick="deIncrement(1); return false" value="Coundown by 1">
<input type="button" id="inc2" onclick="deIncrement(2); return false" value="Coundown by 2">
<input type="button" id="inc3" onclick="deIncrement(3); return false" value="Coundown by 3">
<input type="button" id="inc4" onclick="deIncrement(4); return false" value="Coundown by 4">
There are a few issues with your code...
First you need to set a function to window.onload, not call the function and put the result in window.onload.
Next as briosheje said, you cannot have maxTime as a property and a function of the same object (even if you're using the prototype). Just use the function to get the latest value every time.
Be sure to use this.myFunction() (as opposed to myFunction()) to use another function/object from the object you're currently in.
Finally, your jsFiddle needed to run directly in the <head> element in order to use window.onload and named functions that are accessed from inline HTML attributes (ie. deIncrement). This was a configuration option set on the left-top of the page.
See the updated jsFiddle: http://jsfiddle.net/g4Lu3n43/2/

Categories