Unable to get angular button hold loop to work properly

My goal is to create a button that can be pressed once to execute a single command, but also has the capability to hold the button down and execute the command multiple times while still holding it. I am working with AngularJs (though I don't believe this is related to the issue).

This is what I have attempted so far:

<button type="button" 
        class="btn btn-default" 
        ng-click="ChangeSetPoint('Up')"
        ng-mousedown="startLoopingUp()"
        ng-mouseup="stopLoopingUp()"
        ng-mouseleave="stopLoopingUp()">
        +
</button>

and in the controller:

$scope.ChangeSetPoint = function(direction){
            //Code to change the setpoint
        }

        var looping = false;
        var promise;
        $scope.startLoopingUp = function(){
            looping = true;
            promise = setTimeout(loop('Up'),1000);           
        }

        var loop = function(direction){                                         
            $scope.ChangeSetPoint(direction);
            if(looping){
                promise = setTimeout(loop(direction),300)
            }
        }

        $scope.stopLoopingUp = function(){
           looping = false;
           clearTimeout(promise);
        }

Initially, everything was functioning until I introduced the 'direction' parameter. Previously, I utilized arguments.callee in setTimeout, but upon researching how to pass an argument with that function, I discovered that the use of arguments.callee is discouraged (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee). Since then, I have been encountering 'Maximum call stack size exceeded' errors.

Answer №1

I previously utilized the function below in a directive. When creating it, I had the following objectives in mind:

This function is capable of accommodating three distinct callback functions. The "short" callback gets triggered with a single click. Holding down the button triggers the short callback repeatedly. Continuously holding down the button activates the "long" callback repetitively. Finally, when the user releases the button, a third "final" callback is executed.

While this may not provide an exact solution to your issue, it could serve as inspiration and guidance for you :) Best of luck.

/**
  *
  * @param {Event} evt
  * @param {Function} shortCallback
  * @param {Function} longCallback
  * @param {Function} [finishCallback] optional
  */
var onBtnClick = function (evt, shortCallback, longCallback, finishCallback) {
    //prevent mobile browser from long tap behaviour (simulated right click)
    evt.preventDefault();
    //only react to left mouse button or a touch event
    if (evt.which === 1 || evt.type === "touchstart") {
        //save 'this' context and interval/timeout IDs
        var self = this,
            short = {
                timeout     : null,
                interval    : null,
                callback    : angular.isFunction(shortCallback) ? shortCallback : angular.noop
            },
            long = {
                timeout     : null,
                interval    : null,
                callback    : angular.isFunction(longCallback) ? longCallback : short.callback
            },
            listener = "mouseup mouseleave touchend touchcancel",
            //
            cancelShort = function () {
                $timeout.cancel(short.timeout);
                $interval.cancel(short.interval);
            },
            //
            cancelLong = function () {
                $timeout.cancel(long.timeout);
                $interval.cancel(long.interval);
            };

        //react to a single click
        short.callback();

        //when user leaves the button cancel timeout/interval, lose focus and unbind recently bound listeners
        self.one(listener, function (e) {
            e.preventDefault();
            cancelShort();
            cancelLong();

            if (angular.isFunction(finishCallback)) {
                finishCallback();
            }

            self.blur();
        });

        //on a long click call the callback function within an interval for faster value changing
        short.timeout = $timeout(function () {
            short.interval = $interval(short.callback, 50, 0, false);
        }, 300, false);

        //when pressed even longer, cancel previous callback and fire "long" one
        long.timeout = $timeout(function () {
            cancelShort();
            long.interval = $interval(long.callback, 50, 0, false);
        }, 1500, false);
    }
};

This function has been attached to an element using the following method:

/**
 *
 * @param {String} selector
 * @param {Function} clickCallback
 * @param {Function} fastCallback
 * @param {Function} [finishCallback] optional
 */
 var bindEvent = function (selector, clickCallback, fastCallback, finishCallback) {
     $element.on("mousedown touchstart", selector, function (evt) {
         onBtnClick.call($(this), evt, clickCallback, fastCallback, finishCallback);
     });
 };

Answer №2

The issue stemmed from the parameter:

when making a change

setTimeout(loop, 1000) to setTimeout(loop('Up'), 1000)

I mistakenly passed the return of the function as a parameter, instead of passing the function itself.

The correct approach would have been:

promis = setTimeout(function(){ loop('Up') },1000); 

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Use Node.js to transform every spreadsheet into Json format

I have been attempting to convert an Excel file into JSON format. I tried utilizing the "xls-to-json" npm package with the following code: node_xj = require("xls-to-json"); node_xj({ input: "Auto.xlsx", // input xls output: "output.json", // output ...

Postal Code Auto-Suggest by Google

I'm attempting to create a unique autocomplete feature for a text box that specifically provides postal codes. To accomplish this, I have followed the guidelines outlined in the documentation found at https://developers.google.com/places/webservice/au ...

Unable to reach elements that have been loaded through ajax with jQuery

I am facing an issue where I cannot access content loaded via ajax for modification. Unfortunately, I do not have access to the js file responsible for the initial load. Therefore, I need to create a separate function to alter the content. The required mo ...

Froala Editor experiencing crashes during editing process

When utilizing the Froala editor in AngularJS 1.6, I encountered a problem where adding HTML in code view and attempting to edit it in view mode caused the editor to crash. Interestingly, this issue did not arise on my local system when using WampServer w ...

Tips for creating a static PHP website with a fixed navigation bar and footer

I am looking to create a webpage with a consistent horizontal navigation bar and footer, but where the content changes dynamically based on the user's interactions with the navigation. For example: <div class="nav-bar"> /* The navigation bar r ...

In JavaScript, split each element in an array into individual elements

Is there a way to split elements separated by commas into an array in JavaScript? ["el1,el2", "el3"] => ["el1", "el2", "el3"] I am looking for a solution to achieve this. Can you help me with that? ...

What is the process for choosing every child (regardless of level) of a parent using jQuery?

Is there a way to unbind all elements from a parent node using jQuery? How can I effectively select all children, regardless of their nesting level, from a parent node? I attempted the following: $('#google_translate_element *').unbind('c ...

Neglecting to pass data to the controller through the onclick function

Using this specific button element in my JavaScript implementation has raised some concerns. <button id="bid" type="button" class="button" onclick="connect()">Save</button> While I have noticed that it displays an alert message when 'ale ...

Using websockets in a React client application

Attempting to establish a connection with a backend layer running on localhost, here is the provided source code: const { createServer } = require("http"); const cors = require("cors"); const photos = require("./photos"); const app = require("express")( ...

Row in Internet Explorer 7

I have a function that reveals hidden rows in a table, which works perfectly in all tested browsers except for IE7. The function is written using Prototype.js. function showInactives(){ var row_list = $$('tr.inactive'); var ck =$('inactive_ ...

Searching for a substring within a larger string in JavaScript

I am trying to create a loop in JavaScript (Node.js) that checks if a string contains the "\n" character and then splits the string based on that character. <content>Hello </content><br /> <content>bob </content><br / ...

Analyzing the structure according to the month/week/year

My array consists of count and date values: day = [ { count: 1, date: '2022-07-07' }, { count: 1, date: '2022-08-14' }, { count: 2, date: '2022-07-19' }, { count: 4, date: '2022-07-19' }, { count: 2, date: ...

preventing users from selecting dates and times after the current date on angular bootstrap datepicker

Is there a way to block future dates and times in the angular bootstrap datepicker? I have come across some examples that only block until 4 hours before. What I am looking for is to block until the current hour and minute Is it possible to achieve this? ...

React is throwing an error that says, "You cannot use the import statement outside a

My journey with learning React has just begun. I followed the steps outlined in the starting guide at https://react.dev/learn/add-react-to-an-existing-project, but unfortunately, I encountered an error: "Cannot use import statement outside a module." Here ...

Typescript method fails to compile due to an indexing error

Imagine you're trying to implement this method in Typescript: setResult(guId: string,fieldname: string, data:Array<UsedTsoClusterKey>) { let octdctruns: OctDctRun[] = [...this.octDctRuns]; const index = octdctruns.findIndex((o) => o.guid ...

Constructor-generated element doesn't reflect changes upon component re-rendering

Why doesn't the <select> I create in the constructor update correctly when I select a different flavor? The other select and text update, but not this one. class ConstructorComponent extends React.Component { constructor() { super(); ...

What is the best method for loading multiple HTML files into a Div container?

Recently, I made the decision to improve the look of an online manual I have been working on for my company by incorporating Bootstrap. The manual is structured with a tree-view that contains titles linking to HTML files with information and CSS stylesheet ...

Examining the initialization of an angular controller under various circumstances

In my project, I have a controller that has a dependency on a service. During initialization, the controller calls a function on the service. To illustrate this point, here is a simplified example: describe('tests', function() { var _scope, ...

What is the most effective way to use Javascript to update an image depending on the selected value in a dropdown

My JavaScript skills are limited, but I have successfully created a form with 4 drop-down selection boxes on the left side for users to choose from (State, Car, Year, Condition). After making their selections, users can click a 'calculate' butto ...

Creating a form with varying lengths using AngularJS

I am looking to enhance a basic angularjs form by making it capable of having a flexible number of inputs. Essentially, I want to allow users to add one or more siblings to the form by clicking a + button, which will then create additional sibling input fi ...