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>
Related
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...
First let me describe the project I'm stuggeling with. English is not my foreign language so I do not know the exact name of the effect I am looking for.
Basicly I am creating a bingo type of game where the host can press on a button to start the bingo wheel. Eventually the wheel will stop and the word it has landed on is the chosen word for that round. After that, this word get removed from the game and the game starts over and over untill someone calls a bingo.
I started playing a bit around with some JSON data and how to remove the items from the array etc which is pretty easy to do. But now comes the part where I am struggeling the most. I have to create a kind of function that scrolls through all options like a wheel at a certain speed that eventually decreased in speed so it will land on the chosen word for this round. I created a fiddle with the code I currently have. Note that it's purely created for functionality instead of looks!
var json = {
"titles": [
"PLACEHOLDER_1",
"PLACEHOLDER_2",
"PLACEHOLDER_3",
"PLACEHOLDER_4",
"PLACEHOLDER_5",
"PLACEHOLDER_6",
"PLACEHOLDER_7",
"PLACEHOLDER_8",
"PLACEHOLDER_9",
"PLACEHOLDER_10",
"PLACEHOLDER_11",
"PLACEHOLDER_12",
"PLACEHOLDER_13",
"PLACEHOLDER_14",
"PLACEHOLDER_15"
]
}
$(document).ready(function() {
var app = new Bingo.init();
})
var Bingo = {
viewport: {
isMobile: 0,
isTablet: 0,
isDesktop: 1,
device: 'desktop',
browser: null
}
}
Bingo.init = function() {
Bingo.gameController.init();
};
Bingo.gameController = {
gameNames: {},
init: function() {
Bingo.gameController.general.saveJson();
$('.test').on('click', Bingo.gameController.array.pickRandomNumber)
},
general: {
saveJson: function() {
Bingo.gameController.gameNames = json.titles;
},
//General reset function
resetGame: function() {
Bingo.gameController.general.saveJson;
}
},
array: {
pickRandomNumber: function() {
//reset gamefield
Bingo.gameController.game.buildGame();
var gameNames = Bingo.gameController.gameNames;
var totalNames = gameNames.length;
//Pick a random number
var chosenNumber = Math.floor(Math.random() * totalNames);
Bingo.gameController.array.remove(chosenNumber)
},
remove: function(id) {
//remove chosen name from array
var gameNames = Bingo.gameController.gameNames;
var check = gameNames.indexOf(gameNames[id]);
Bingo.gameController.game.highlightName(id);
if (check != -1) {
gameNames.splice(check, 1);
Bingo.gameController.gameNames = gameNames;
}
}
},
game: {
buildGame: function() {
//build all the array entry's into the div
$('.page.main-game').empty();
var gameNames = Bingo.gameController.gameNames;
for (var i = 0; i < gameNames.length; i++) {
var item = '<div class="name-item" data-id="' + i + '">' + gameNames[i] + '</div>';
$('.page.main-game').append(item);
}
},
highlightName: function(id) {
//highlight the chosen number red
$('.name-item[data-id="' + id + '"]').css('color', 'red');
}
}
}
Fiddle link here
(I hope the link is correct, not using fiddle that much)
So now when you click on the 'play again' button you see that it wil highlight a word. What has to happen is when I press the play again button the red highlight has to go from the first div to the last and so forth untill it eventually stops at a div (which is chosen with the random number or something).
If someone can help me with this or could give me a hint in the right direction please let me know!
EXTRA: The app will eventually get a look like a scrollwheel that the iphone gets when you open a select box (hope you know what I am referring to). So thats why its a wheel of fortune-ish effect. If someone could provide me with the correct name for this let me know so I can adjust the title!
If any information is missing please let me know, i'd be happy to provide it! Thanks in regard!!
The basic ideas are (1) to keep the current index, so you can start the spin every time from that index, where 'spining' is just increasing that index or set to zero when reaching the maximal index; and (2) set a timeout for the next painting, and reduce that timeout everytime, until it's low enough.
JsFiddle Demo
HTML
<p><button onclick="w.spin(w.randomNext(), 8)">SPIN</button></p>
<ul id='wheel'>
<li>$100</li>
<li>$250,000</li>
<li>$25,000</li>
<li>$10,000</li>
<li>$1,000</li>
<li>$5</li>
<li>$2,000</li>
<li>30,000</li>
<li>$40</li>
</ul>
JavaScript
var wheelEl = document.getElementById('wheel');
function Wheel () {
this.current = 4;
}
Wheel.prototype.init = function () {
this.onItem();
}
Wheel.prototype.randomNext = function () {
return Math.floor(Math.random() * wheelEl.children.length);
}
Wheel.prototype.spin = function (next, itemsPerSecond) {
var timeout = setTimeout(onItem.bind(this), (1 / itemsPerSecond) * 1000);
function onItem () {
// stop if speed is low enough
if (itemsPerSecond < 1)
return;
// spin to next item
this.current ++;
if (this.current >= wheelEl.children.length)
this.current = 0;
// paint text
this.onItem();
// reduce speed
clearTimeout(timeout);
itemsPerSecond--;
timeout = setTimeout(onItem.bind(this), (1 / itemsPerSecond) * 1000);
}
}
// paints the index of this.current
Wheel.prototype.onItem = function () {
for (var i = 0 ; i < wheelEl.children.length ; i++)
wheelEl.children[i].style.color = (i == this.current) ? '#6d4321' : '#000000';
}
var w = new Wheel();
w.init();
It only increase or decrease when the click is pressed and moved from top to bottom, That is normal operation.
I have this script that allows dragging from the Input increment and decrement value, this works well in Firefox but in Chrome does not work correctly, can anyone correct this bug (in Chrome)?
I have problems even with the script. It only works in firefox, but it does not work the same in Chrome.
Example: http://jsfiddle.net/iLen/bacx5qem/
Video: http://recordit.co/yW1NUPrXBP (left: chrome / right: firefox)
var clicking = false;
$('.vamoAlSubibaja').mousedown(function() {
clicking = true;
});
$(document).mouseup(function() {
clicking = false;
})
var i = 0;
var y = 0;
$('.vamoAlSubibaja').mousemove(function(my) {
if (clicking == false) {
return
} else {
// change value
if (my.pageY <= $(this).offset().top + $('.vamoAlSubibaja').css('width').replace('px', '') / 10) {
y = parseInt($(this).val()) + 1;
$('.movestatus').text('plus');
} else {
y = parseInt($(this).val()) - 1;
$('.movestatus').text('minus');
}
$(this).val(parseInt(y));
i++;
}
});
It's not working because you are assigning the mousemove event to the div, not to the document.
If you want each div to respond independently you need to assign a value to a variable to know which was clicked down. This way when the mouse moves (after clicking down one of the divs) you can assign your function to that specific div.
This demo is only fixing the mousemove so you can see how to solve your main issue.
var clicking = false;
$('.vamoAlSubibaja').mousedown(function() {
clicking = true;
});
$(document).mouseup(function() {
clicking = false;
})
var i = 0;
var y = 0;
$(document).mousemove(function(my) {
if (clicking == false) {
return
} else {
// change value
if (my.pageY <= $('.vamoAlSubibaja').offset().top + $('.vamoAlSubibaja').css('width').replace('px', '') / 10) {
y = parseInt($('.vamoAlSubibaja').val()) + 1;
$('.movestatus').text('plus');
} else {
y = parseInt($('.vamoAlSubibaja').val()) - 1;
$('.movestatus').text('minus');
}
$('.vamoAlSubibaja').val(parseInt(y));
i++;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<i>Only when the mouse moves up and down does
the value change from the input
(validate min y max)</i>
<br />
<br />
<br />
<br />
<br />
<input type="text" value="5" class="vamoAlSubibaja" id="change_value_up_down" />
<input type="text" value="5" class="vamoAlSubibaja" id="change_value_up_down2" />
<!-- only ref -->
<span class="clickstatus"></span>
<span class="movestatus"></span>
As per the official DOM Level 3 documentation
In some implementation environments, such as a browser, mousemove events can continue to fire if the user began a drag operation (e.g., a mouse button is pressed) and the pointing device has left the boundary of the user agent.
In this case Firefox continues to fire the mousemove event when the mouse leave the input limits, and Chrome doesn't. That's why you should follow the solution by #Alvaro as a reference and use the document instead the input.
Keep in mind as well that
Implementations are encouraged to determine the optimal frequency rate to balance responsiveness with performance.
So the effect wont look exactly the same across browsers.
I can see there are a couple of similar questions asked here but unfortunately I couldn't find the answer I expect.
I am quite new to Programming and trying my hands on Javascript Progress Bar. I have a counter to countdown whenever the progress bar runs out of width but i got the problem, when the tab in focus is inactive, the progress bar pauses thereby keeping counter not to countdown.
I got the idea of using web workers http://www.tutorialspoint.com/html5/html5_web_workers.htm but I couldn't get that to work. I would appreciate any form of help I get here.
Below is my Code.
<!DOCTYPE html>
<html>
<head>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/
jquery.min.js">
</script>
<style>
#progressContainer {
position: relative;
width: 97%;
height: 25px;
background-color: #ddd;
margin-bottom: 15px;
}
#progressBar {
position: absolute;
width: 0%;
height: 100%;
background-color: #A9A9A9;
}
#container{
width: 200px;
height: 200px;
border: 1px solid black;
padding: 10px
}
</style>
<script>
$(document).ready(function(){
$("#countDownBtn").click(function(){
var cdNumber = $("#countDownId").val();
var id = setInterval(frame, 100);
var elem = document.getElementById("progressBar");
var progressBarWidth = 101;
function frame() {
if (progressBarWidth === 0) {
clearInterval(id);
cdNumber--;
$("#countDownId").val(cdNumber);
console.log(cdNumber);
if (cdNumber === 0) {
clearInterval(id);
}
else {
elem.style.width = '100%';
progressBarWidth = 100;
//alert("Hi");
}
}
else {
progressBarWidth--;
elem.style.width = progressBarWidth + '%';
}
}
});
});
</script>
</head>
<body>
<div id="container">
<div>
<input type="text" id="countDownId" value="">
<button id="countDownBtn" class="btn">Click</button>
</div><br>
<div id="progressContainer">
<div id="progressBar"></div>
</div>
</div>
</body>
You'll always run into such problems when depending on the precision of some interval; even requestAnimationFrame. They ain't precise.
The better approach (not just in this case, but pretty much every time you have to transition a value over time) is to save the startTime and compute the passed time in the interval (as #Nosyara already suggested).
When dealing with scaling-factors and/or pausing of this stuff, things can get messy again. Here a utility for this task:
// The basic concept of this "Clock" is a linear equation over Date.now()
// plus the logic to make this equation pausable.
// therefore it's completely independant of **any** interval; it's just math.
function Clock(v){
var p=true,m=1,b=+v||0,n=Clock.now;
return Object.assign(Object.create(Clock.prototype),{
// getter() / setter(value)
// I don't use real getter and setter, because this syntax
// allows/implements method-chaining
value(v){return arguments.length?(b=(+v||0)-(!p&&m*n()),this):b+(!p&&m*n())},
speed(v){return arguments.length?(v=+v||0,p||v===m||(b+=n()*(m-v)),m=v,this):m},
paused(v){return arguments.length?(((v=!!v)===p)||(b+=n()*((p=v)?m:-m)),this):p},
});
}
Object.assign(Clock.prototype,{
valueOf(){return this.value()},
//aliases for setting the paused() state; doesn't matter if you call them repeatedly.
start(){return this.paused(false)},
stop(){return this.paused(true)},
});
Clock.now=Date.now; //the function used for timing
//Clock.now=performance&&performance.now?performance.now.bind(performance):Date.now;
Now to your code:
$(function(){
function frame(){
//yes, countDown get's converted to Number
var value = Math.max(0, countDown);
var count = Math.ceil(value);
var progress = value % 1;
$progressBar.width( progress * 100 + "%" );
//so that I don't update $countDown.val() on every frame, but only if necessary
//on the other hand, it wouldn't be that bad.
if(count !== lastCount) $countDown.val( lastCount = count );
//either stop the countDown or request the next frame.
if(value > 0) requestAnimationFrame(frame);
else countDown.stop();
}
//create a Clock and set speed. Clock is paused by default.
var countDown = Clock().speed( -1 / 10000/*ms*/ );
var $progressBar = $("#progressBar");
var $countDown = $("#countDownId");
var lastCount;
$("#countDownBtn").click(function(){
//if !countDown.paused() then there already is a pending `requestAnimationFrame(frame)`
//from the last call of frame()
if(countDown.paused()) requestAnimationFrame(frame);
countDown.value( $countDown.val() ).start();
});
})
I had similar problems in one of my projects in Chrome browser. The root of problem, that Chrome allows up to 1 timer event per second (setTimeout or setInterval) if tab is not active. In case there more than 1 call per second - it creates queue, than behavior of page depends on logic inside events and may look not as expected. One of solutions is to check visibility of the page and manage intervals Check here
As #ManoDestra pointed out in the comment, you should use requestAnimationFrame instead of setInterval.
The best solution I would propose is utilizing the HTML5 Visibility API to detect when a tab becomes active again after being inactive, and then update the progress bar accordingly. Perhaps you could store the timestamp of the countdown when it is initialized, and when the tab becomes active again, you look at the new timestamp and make a comparison.
You can use setTimeout instead and use recursion. It could look something like this:
var stopInterval = false;
frame();
function frame() {
if(stopInterval) return;
// Your stuff to run in interval HERE
setTimeout(frame, 100);
}
Webworkers are not supported in older browsers. Also browsers specify how they handle setTimeout and setInterval on inactive tab individually, so the behavior may differ. Chrome seems to slow down the recursion alot (1 per second?).
Specifically in your case you can use clock time to represent right progress. When you don't want trust browser about interval events.
Let say you want 10 sec countdown:
var tm = new Date().getTime() + 10000; // 10 sec in milliseconds
setInterval(function(){
var secondsPassed = (tm - new Date().getTime()) / 1000;
// update UI
}, 100); // You can use variable here in different visibility modes
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!