views:

983

answers:

5

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.

A: 

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>
tj111
A: 

I posted the above into my site and I'm still unable to press and hold either button to have the value increase or decrease until I release the mouse click. (Original: http://www.blackbird502.com/white.htm ) (Revised: http://www.blackbird502.com/white1.htm ) I am trying to replace a YUI slider with the buttons and text field. Thanks for your help. And pardon my ignorance.

I attempted pasting your javascript in addition to my original script as well, and observed no behavior change from the original. I assume I'm missing something pretty basic.

couchua
+1  A: 

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);
}
Sidnicious
This works perfectly: http://www.blackbird502.com/white2.htmTHANKS!
couchua
Any chance there would be a way to put a min/max limit on the "amount," like 0-1000 or so?
couchua
I updated the answer to add limits. I tried to make this code fairly readable, so by all means, play with it, break it, and improve it. That's the best way to learn any language.
Sidnicious
+1  A: 

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>
Jesse Brown
Ooops, my code sets the onmouseup event to the button so has the bug that SydneySM refers to in his solution.
Jesse Brown
I just worked this into the page, and it works perfectly: http://www.blackbird502.com/white1.htmI will try out SidneySM's below to try and avoid the 'bug,' though there is no evidence of a 'bug' from a novice's stand point. THANKS!
couchua
I changed the code to fix the bug. I just attached the onmouseup event to the document. Credit goes to SidneySM for catching that.
Jesse Brown
A: 

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!

cmaunu