0

I have a working tool (see first code block), where I would like to include click-and-hold functionality.

I would like to to rapidly ADD when left button is held, and rapidly SUBTRACT when right button is held. Just like the single click functionality it already has, but a faster option.

I am a complete novice, so working demos are greatly appreciated. Thank you.

Code:

<!DOCTYPE html>
<html>
<head>
<script>

var Alexander = 
     {
      Strength: "AlexanderStrengthVal",
      Bonus: "AlexanderRemainingBonusVal",
      Limits: {
        Strength: { 
          max: 80,
          min: 60
        }
              }
    };

function add(character, stat)
{
  var txtNumber = document.getElementById(character[stat]);
  var newNumber = parseInt(txtNumber.value) + 1;
  if(newNumber > character.Limits[stat].max) return;
  var BonusVal = document.getElementById(character["Bonus"]);
  if(BonusVal.value == 0) return;
  var newBonus = parseInt(BonusVal.value) - 1;
  BonusVal.value = newBonus; 
  txtNumber.value = newNumber;
}

function subtract(e, character, stat)
{
  e.preventDefault();
  var txtNumber = document.getElementById(character[stat]);
  var newNumber = parseInt(txtNumber.value) - 1;
  if(newNumber < character.Limits[stat].min) return;
    var BonusVal = document.getElementById(character["Bonus"]);
  var newBonus = parseInt(BonusVal.value) + 1;
  BonusVal.value = newBonus; 
  txtNumber.value = newNumber;
}

</script>  
</head>
<body>

  <table cellpadding='5' border='1' style="text-align:center; color:#ffffff; background-color:#444444; font-family:arial; font-size:14px">
   <tr>
      <td><b>Character</b></td>
      <td><b>Strength</b></td>
      <td><b>Spending Bonus</b></td>
   </tr>
    <tr>
      <td>Alexander</td>
      <td>
        <input 
        id="AlexanderStrengthVal" 
        type="text" value="60" 
        style="width:30px; border:none; color:#ffffff; background-color:transparent; text-align:center" 
        onfocus="this.blur()" 
        onClick="add(Alexander, 'Strength')" 
        onContextMenu="subtract(event, Alexander, 'Strength');"   
        />
      </td>

      <td>
        <input 
        id="AlexanderRemainingBonusVal" 
        type="text" 
        value="30" 
        style="width:30px; border:none; color:#ffffff; background-color:transparent; text-align:center" 
        />
      </td>

    </tr>
  </table>
</body>
</html>
tshepang
  • 12,111
  • 21
  • 91
  • 136
user2811882
  • 67
  • 1
  • 2
  • 8
  • 1
    Thus, the problem with trying to bolt different bits of code together! I'm not sure what exactly you are trying to do with the click and hold functionality, but in theory you should be able to assign handlers to the document rather than a button. – Darren Crabb Oct 16 '13 at 17:16
  • 2
    In which case the code you found is not going to work unless you have buttons. You'll need to investigate mouse events to do it without buttons ... see this http://www.w3schools.com/jsref/event_onmousedown.asp and check out the examples for detecting which button was pressed. – Darren Crabb Oct 16 '13 at 17:39

2 Answers2

1

How about attaching timer functions that use .setTimeout(), .setInterval() to .onmousedown, .onmouseup, .onmouseout hooks, have you tried that approach?

Or try this ready to go function I use for similar tasks. fiddle.

//
// #Timer
//
// if you are unfamiliar with this code construct,
// ( I think it is called 'module' pattern IIRC )
//   this is base structure behind it:
//
//  defines/runs/sets_context/passes_arguments of anonymous function in one go
//  makes 'safe' ( private ) scope for module definition
//  module assignment is performed at the top
//  where it is easy to spot, rename, and shift around where needed
//  'factory' is a function that is supposed to return whatever the 'module' is
//  be it a function, object, or whatever,
//  and to assign it to arbitrary 'host' object,
//  providing room to rename it in case of naming collisions
//
//  ;(( function ( name, factory ) {
//
//    // this === module's context
//
//    this[name] = factory();
//
//  } ).call(
//
//    hostObject,        // object to attach module to
//   "customModuleName", // arbitrary name to use for it
//   function () {       // factory method that is supposed to define/return ( preferably independent ) piece of functionality
//
//     // save to do whatever is required in this scope
//     // without the impact on globals.
//     // declare identifiers, etc.
//     // whatever this function returns is assigned to context above
//
//     function doStuff () {
//       return Math.random() > .5;
//     }
//
//     var
//        _api =
//        {
//          props    : 1,
//          methods  : function () {
//            var stuff;
//            stuff = doStuff();
//            return stuff;
//          }
//        };
//
//     // ...code
//
//     // and return whatever the module's goal is
//     return _api;
//
//   }
//
//  ));
//
;(( function ( field, dfn ) {
  // add 'Timer' identifier to global scope
  this[field] = dfn();
} ).call(
 window,  // object to asign function to
 "Timer", // property name to use
 function () {

    // helpers and
    // shortcuts for use by code bellow

    var undef; // === undefined
    var aproto = Array.prototype;
    var _ = {

      // used to filter functions in timer api arguments
      isfn     : function ( o ) {
        return typeof o == "function";
      },

      // is provided parameter an object
      isobj    : function ( o ) {
        return o === Object( o );
      },

      // checks provided parameter,
      // returns false for: null, undefined, NaN
      // used to check if parameter is passed to timer methods
      isvalid  : function ( o ) {
        return ( o != null ) && ( o === o );
      },

      // removes array elements that match passed arguments
      // supposed to be used through .call/.apply function method:
      // _.gc.call( arr, "a", "b", "c" ), etc.
      // iterates array backward, '.splice-ing' it by 1 if current value
      // matches one of provided arguments
      // returns gc-ed array
      // used by .off() method to remove scheduled function(s)
      gc       : function () {

        // this === ( array ) target_array_to_cleanup

        // provided arguments[] to remove
        var togc = _.slice( arguments );

        for (
          var i = this.length;
          --i  >= 0;
          ( togc.indexOf( this[i] ) != -1 )
          && this.splice( i, 1 )
        );
        return this;
      },

      // empties passed array and returns it
      // used by .off() method to remove scheduled functions
      empty    : function ( arr ) {
        return ( arr.length = 0, arr );
      },

      // loops array
      // uses native .forEach if available
      // otherwise simulates the behaviour
      // returns iterated array
      // used by .fire() method to loop through registered function[] and
      // to execute them in order
      arreach  : ( function () {
        return ( typeof aproto.forEach == "function" )
              ? function ( arr, fn ) {
                  arr.forEach( fn );
                  return arr;
                }
              : function ( arr, fn ) {
                 for (
                    var i = 0,
                    l = arr.length;
                    i < l;
                    ( i in arr )
                    && fn.call( this, arr[i], i, arr ),
                    i++
                 );
                 return arr;
                };
      } )(),

      // substitues content of array ( arr )
      // with provided array's content ( arrnew )
      // returns modified array ( arr )
      // used by .start() method to quickly switch timer handler arguments
      // if any are passed to it
      arrsub   : function ( arr, arrnew ) {
        return ( aproto.push.apply( _.empty( arr ), arrnew ), arr );
      },

      // loops own properies of an object and retuns it
      // used by .off() method, ant _.init() function
      // to perform appropriate tasks ( object cleanup/property_assignment )
      owneach  : function ( obj, fn ) {
        for (
          var l in obj
        ) {
          _.owns( obj, l )
          && fn.call( obj, l, obj[l] );
        }
        return obj;
      },

      // asyns a function
      // returns new function based on provided one
      // that will run asyncrounously
      // used to async 'tick' handlers
      async    : function ( fn ) {
        return function () {
          var host   = this;
          var args   = _.slice( arguments );
          var origfn = fn;
          setTimeout(
            function () {
              origfn.apply( host, args );
            }
          );
          return this;
        };
      },

      // asigns properties of an object ( propsObj )
      // to provided ( obj ) object
      // used in couple of places to quickly assign properties/values to objects
      init     : function ( obj, propsObj ) {
        _.owneach(
          propsObj,
          function ( field, value ) {
            obj[field] = value;
          }
        );
        return obj;
      },

      // core {}.hasOwnProperty shortcut
      owns     : function ( obj, field ) {
        return Object( obj ).hasOwnProperty( field );
      },

      // native [].slice shortcut
      // used to turn dynamic arguments[] to real array
      slice    : function ( args, m, n ) {
        return aproto.slice.call( args, m, n );
      },

      // empties an object
      // used by .off() method to empty evHandler functions cache
      vacate   : function ( obj ) {
        for (
          var l in obj
        ) {
          try {
            _.owns( Object.prototype, l )
            || ( delete obj[l] );
          } catch( xc ) {}
        }
        return obj;
      }
    };

    // main function uses this strings
    // for subscribing/removing/executing handlers
    var timerEvent = {
      start : "tickStart",
      stop  : "tickStop",
      tick  : "tick",
      end   : "tickEnd"
    };
    return (function ( listener ) {


      // main timer function
      // @param1, float, optional, how often to fire 'tick' events, default == 1000, ( == 1sec )
      // @param2, integer, optional, how many times to fire 'tick' event, default == Infinity
      // returns, Object, object with following api:
      //
      //  registering functions for 'timerEvent' events:
      //
      // .on( evType, func )
      //   # caches a function for given event type ( of 'timerEvent' object )
      // .off( [ evType, func] )
      //   # unregisteres function for given event type
      //   # if no function is provided removes all functions registered for event 'evType'
      //   # if nothing is provided removes all registered functions
      // .fire( evType [, ...params_for_registered_handlers ] )
      //   # runs functions registered for given event type
      //   # passing provided arguments to them, if any
      //   # used internaly when .start() method is called
      //
      //  these are for controling timer object:
      //
      // .start([ ...params])
      //   # starts triggering 'tick' events updating internal state
      //   # passes provided parameters to triggerd functions,
      //   # ( available through .data property of object passed to handlers )
      // .stop()
      //   # pauses triggering 'tick' events ( if Timer object is in 'running state' )
      //   # triggers 'tickStop' event
      // .reset()
      //   # nulls internal state, stops dispatching 'ticks', resets counter
      //   # triggers 'tickEnd' event
      //
      //  these are for quering internal timer state:
      //
      // .currentCount()
      //   # how many times Timer fired so far
      //   # returns, integer
      // .delay()
      //   # gives timer's delay
      //   # returns, float
      // .repeatCount()
      //   # how many times timer will dispatch 'tick' events
      //   # returns, integer
      // .running()
      //   # 'is running' state
      //   # returns, boolean
      //
      return function ( delay, fireCount ) {
        return (function ( delay, fireCount ) {

            // preconfigured object that will handle 'tick' events
            var host = this;

            // timer object's internal state
            // used/updated by timer
            var timerState =
            {
              currentCount : 0,
              delay        : Math.abs( parseFloat(delay) )   || 1000,
              repeatCount  : Math.abs( parseInt(fireCount) ) || Infinity,
              running      : false,
              interval     : undef
            };

            // hack to reset .currentCount property asynchronously in .reset() method
            // without using it this way, ( by zeroing it directly, like: timerState.currentCount = 0 )
            // will make last 'tick' function call report: .currentCount() == 0,
            // instead of whatever the currentCount was
            // at the time the 'last tick' dispatch was performed
            //
            // because the handlers are runned asyncronously, and
            // because setting currentCount to 0 manualy ( synchronously )
            // will reset it before last function gets to be runned
            // reseting it asyncronously schedules a function to reset a property
            // making 'last tick' function report correct currentCount ( not 0 ).
            var zer0CurrentCount =
            _.async(
              function () {
                timerState.currentCount = 0;
              }
            );

            // holds arguments passed to last .start() call
            // uses these for further .start() calls if no new arguments are given
            // passes them to triggered functions
            var fireargs = [];

            // attaches timer api
            // ( .start, .stop, .reset, .currentCount, .delay, .repeatCount, .running )
            // to timer object
            _.init(
              host,
              {

                // handles starting event dispatch
                // if arguments are given, caches and
                // uses them for further calls
                // 'keeps an eye' on timer's configuration options
                // updating/aborting dispatch when/if necessary
                // triggering corresponding events and functions
                // does nothing if timer is already in 'active' state
                start: function () {
                  var startargs;
                  host.running()
                  || (
                    timerState.running = true,
                    ( startargs = _.slice( arguments ) ).length
                    && _.arrsub( fireargs, startargs ),
                    host.fire.apply(
                      host,
                      [timerEvent.start]
                      .concat( fireargs )
                    ),
                    timerState.currentCount += 1,
                    host.fire.apply(
                      host,
                      [timerEvent.tick]
                      .concat( fireargs )
                    ),
                    ( timerState.currentCount == timerState.repeatCount )
                    && host.reset()
                    || (
                      timerState.interval =
                      setInterval(
                        function () {
                          timerState.currentCount += 1;
                          host.fire.apply(
                            host,
                            [timerEvent.tick]
                            .concat( fireargs )
                          );
                          ( timerState.currentCount >= timerState.repeatCount )
                          && host.reset();
                        },
                        timerState.delay
                      )
                    )
                  );
                  return host;
                },

                // pauses running functions ( if timer{} is in 'running' state )
                // fires 'tickStop' event
                // passes arguments ( given in .start call ) to cached functions
                // updates timer's internal state
                stop: function () {
                  host.running()
                  && (
                    ( timerState.interval !== undef )
                    && (
                      clearInterval( timerState.interval ),
                      timerState.interval = undef
                    ),
                    timerState.running = false,
                    host.fire.apply(
                      host,
                      [timerEvent.stop]
                      .concat(fireargs)
                    )
                  );
                  return host;
                },

                // cancels event dispatch
                // nulls internal state
                // fires 'tickEnd' functions
                reset: function () {
                  ( timerState.interval !== undef )
                  && (
                    clearInterval( timerState.interval ),
                    timerState.interval = undef
                  );
                  timerState.running = false;
                  host.fire.apply(
                    host,
                    [timerEvent.end]
                    .concat( fireargs )
                  );
                  zer0CurrentCount();
                  return host;
                },

                // query timer's current state:

                currentCount: function () {
                  return timerState.currentCount;
                },
                delay: function () {
                  return timerState.delay;
                },
                repeatCount: function () {
                  return timerState.repeatCount;
                },
                running: function () {
                  return timerState.running;
                }

              }
            );
            return host;
        }).call( listener( {} ), delay, fireCount );
      }
    })(

      // function to configure an object to handle custom events
      // gives basic event handling functionality to provided object
      // attaches .on/.off/.fire methods to it,
      // used by main Timer function
      // ( to generate base objects for handling timer events )
      // passed as single ( private ) argument to code above
      function ( obj ) {

          if (
            _.isobj(obj)
          ) {

          // evHandler parameter object is used to store provided handlers in
          (function ( evHandlers ) {

              // plain object to configure for custom event handling
              var host = this;

              // attaches api ( .on, .off, .fire ) to provided object
              _.init(
                  host,
                  {

                    // if function is provided cache it in 'evHandlers' object
                    // ( to be called when needed )
                    // both, event type and function argument, are required
                    on: function ( evtype, fn ) {
                      if (
                        _.isvalid( evtype )
                        && _.isfn( fn )
                      ) {
                        _.owns( evHandlers, evtype )
                        && evHandlers[evtype].push( fn )
                        || ( evHandlers[evtype] = [fn] );
                      }
                      return host;
                    },

                    // deletes a function ( registered for evtype ) from evHandlers{}
                    // both parameters are optional
                    off: function ( evtype, fn ) {
                      if (
                        _.isvalid( evtype )
                      ) {
                        if (
                          _.owns( evHandlers, evtype )
                        ) {
                          _.isfn( fn )
                          && (
                            _.gc.call( evHandlers[evtype], fn ),
                            evHandlers[evtype].length
                            || ( delete evHandlers[evtype] ), 1
                          )
                          || (
                            _.empty( evHandlers[evtype] ),
                            delete evHandlers[evtype]
                          );
                        }
                      } else {
                        _.owneach(
                          evHandlers,
                          function ( evtype, fns ) {
                            _.empty( fns );
                          }
                        );
                        _.vacate( evHandlers );
                      }
                      return host;
                    },

                    // triggers functions registered for ( string ) evtype event
                    // passes 'event{}' to handlers
                    // it holds event type ( .type ),
                    //   data[] ( .data ) provided through .fire call,
                    //   object through which method is called ( .target )
                    //   and current execting function ( .handler )
                    // runs handlers asynchronusly by passing them to
                    // _.async() method before execution
                    fire: function ( evtype ) { // ( any )...args
                      if (
                        _.isvalid( evtype )
                      ) {
                        if (
                          _.owns( evHandlers, evtype )
                        ) {
                            var fireargs = _.slice( arguments, 1 );
                            _.arreach(
                              evHandlers[evtype],
                              function ( evhandler ) {
                              _.async( evhandler ).call(
                                host,
                                {
                                  type    : evtype,
                                  data    : fireargs,
                                  target  : host,
                                  handler : evhandler
                                }
                              );
                              }
                            );
                        }
                      }
                      return host;
                    }
                  }
              );
          }).call(
              obj, // passed object to apply event handling functionality to
              {}   // plain object to cache registered functions in
            );
          }

        // gives back passed/configued object
        return obj;

      }
    );
 }
));
//
// use:
//
// set event dispatch frequency 2x/sec
// run it 5x ( skip second argument to run functions indefinitely )
// check out the console
var timerObj = Timer( 1000 / 2, 5 );

// shortcut function to start the timer
function goTimer () {
  // start 'ticking' functions,
  // pass arbitrary arguments to handlers
  timerObj.start(
    Math.random(),
    ( new Date ).valueOf()
  );
}

// register functions for 'tick' cycle
timerObj
.on(
  "tickStart",
  function ( e ) {
    console.clear();
    console.log(
      e.type + ", \n",
      ( new Date ).valueOf()
    );
  }
)
.on(
  "tick",
  function ( e ) {
    // updateStuff();
    console.log(
      e.type + "#1, "
      , "#", this.currentCount(),", "
      , e.data
    );
  }
)
.on(
  "tick",
  function ( e ) {
    // updateStuff();
    console.log(
      e.type + "#2, "
      , "Math.random() < Math.random(): "
      , Math.random() < Math.random()
    );
  }
)
.on(
  "tickEnd",
  function ( e ) {
    console.log(
        e.type + ", \n"
       , "e.target === this: "
        , e.target === this
     , ", e.target === timerObj: "
        , e.target === timerObj
        , "."
    );
    setTimeout( goTimer, 10000 * Math.random() );
  }
);
//
setTimeout( goTimer, 2000 );
//
//
//
//
public override
  • 974
  • 8
  • 17
1

I'd use setInterval and clearInterval and hook them on mousedown and mouseup events. I am using jQuery for this example. I am not adding code to your example, because you are doing the event binding wrong, as I mentioned in one of my other answers.

I thought I might answer this question and also give you an example on how you should bind your events to your DOM and structure your functions.

var mouseDown;
jQuery(document).ready(function($){
  $('#number').on('mousedown',function(e){
    mouseDown = setInterval(add,150,this,1); // 150 is the number of ms you want
                                             // to delay each number addition
  })
  .on('mouseup',function(e){
    clearInterval(mouseDown);
  });
});

function add(n,val){
  var currentValue = parseInt($(n).text());
  $(n).text(currentValue+val);
}

Here's a Working demo

Community
  • 1
  • 1
Sunyatasattva
  • 5,619
  • 3
  • 27
  • 37
  • don't forget to cancel scheduled interval when mousing out of the target element, or you end up adding multiple intervals each time element is activated. try mousing off while holding a button and mousing it down again.. – public override Oct 17 '13 at 01:10