Determining the JavaScript event source within a function's scope: A guide

Query

Can we determine the origin of a function being triggered by either a user event or an async event, without having access to the original event parameter?

Situation

I am faced with the challenge of identifying the event source within a nested function call that is unaware of who initially triggered the event.

This information is crucial for executing actions like displaying a popup or initiating a login redirection. However, since this function is invoked from various places, passing the event parameter in all callers is not feasible.

Note: I cannot pass parameters to the final function. The use of b('timer') is prohibited.

Example:

  <a onclick="b()" >call</a>
   <script>
 
   function a(){
      b();
   }

   function b(){
      final();
   }

   function final(){
      //Is there something like this caller.event.source ?
      console.log(this.caller.event.source)
   }
   
   setTimeout(a,1000);

In this scenario, I aim to retrieve source == 'timer' or 'onclick', or any other relevant information to determine the event's origin.

Update

Following basilikun's method, I have implemented the following solution:

function final(){
    var callerFunction = arguments.callee.caller,
    evtArg = callerFunction.arguments[0];
    while(callerFunction.caller){
        callerFunction = callerFunction.caller;
        if (callerFunction.arguments[0]) {
            evtArg = callerFunction.arguments[0];
        }
    }
    console.log(evtArg&&evtArg.type?'event fired by user':'event async');
}

You can view the code in action on this JSFiddle link

Any alternative approaches suggested?

Answer №1

Throwing an exception within the innermost function and catching it can help determine the call stack of the program.

The method to retrieve the call stack varies depending on the vendor, such as using e.stack in Firefox, e.message in Opera, or through function body parsing in Internet Explorer and Safari. A reliable implementation can be found on eriwen.com.

From what I understand from the brief snippet provided on that website, this concept has evolved into a larger project on Github, which is likely more comprehensive and dependable than the initial snippet.

In the example given, you would utilize:

function b(){
    final();
}
function final(){
   var trace = printStackTrace();
   //output trace
}


//This would be attached as the click handler for the anchor
function anchorHandler(){
   b();
}
setTimeout(function timerCallback(){
    b();
}, 1000);

By checking whether timerCallback or anchorHandler appear in the trace, you can determine the event that triggered the function call.

Answer №2

Once you've ensured that "timer" is being passed in the initial function a, the following code snippet can be used:

Try It Out Here

<a onclick="a()" >call</a>

<script>

function a(){
    b();
}

function b(){
    final();
}

function final(){
    var callerFunction = arguments.callee.caller;
    var evtArg = callerFunction.arguments[0];
    while (callerFunction.caller !== null) {
        callerFunction = callerFunction.caller;
        if (callerFunction.arguments[0]) {
            evtArg = callerFunction.arguments[0];
        }
    }
    console.log(evtArg);
}

setTimeout(function(){a("timer")}, 100);

</script>

This will extract the very last first argument passed within the chain of function calls. If regular events are utilized, it will retrieve the event object. Alternatively, with timeouts, it captures whatever is passed to the initial function.

Keep in mind that this method relies on arguments.callee.caller, which may not be universally supported and could be slower. Additionally, strict mode does not permit its use, as highlighted by robC.

Answer №3

If you're looking to access the function object and add an expando, one option could be to utilize arguments.callee.caller. However, it's important to note that this approach may not work depending on your specific situation. It's worth mentioning that in strict mode, using arguments.callee.caller is considered deprecated.

<a onclick="b()" >call</a>

<script>

   function a(){
      b.calledByA = true;
      b();
   }

   function b(){
      final();
   }

   function final(){
      var caller = arguments.callee.caller;
      console.log(caller.calledByA);
      caller.calledByA = null;
   }

   setTimeout(a, 1000);

</script>

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

Is there a method to update the res object following a couchbase DB call without encountering the error "Error: Can't set headers after they are sent"?

let express = require('express'); let searchRoute = express.Router(); searchRoute.get('/', function(req, res, next) { console.log('1'); databaseCall(function(error, result) { if (error) { res.sta ...

An error occurred when saving data (date and time) retrieved from a different backend service into MongoDB

I am currently working on a project where I need to fetch JSON data from an external backend service and save it locally in my own database. The goal is to improve the loading speed of the data on the frontend, particularly for representing a graph. Here ...

Having trouble retrieving the ng-model value from input in the controller

I am new to Angularjs and I am working with a datepicker in Ionic. After selecting a date, the input field is correctly getting the value of the selected date. However, I am facing an issue when trying to access this value in the Controller using $scope. ...

Turn only one bracket on the accordion

When clicking on a specific header, I want only one chevron to rotate instead of all the chevrons rotating. I am currently unsure how to specify which chevron should rotate individually. My project is in ASP.NET MVC 5 and I am using razor view to loop th ...

Iterate through a collection of objects and classify/sort/categorize them according to the shared key-value pairs

My data includes information about different players. "players": [ { "name": "Molla Wague", "position": "Centre-Back", "jerseyNumber": 13, "dateOfBirth": "1991-02-21", "nationality": "Mali", "contractUntil": "2018-06-30", ...

How can one authenticate an express session when sending a POST request?

Is there a way to verify that a user is sending a post request in order to prevent unauthorized posting to a URL? I am currently using express-session for this purpose, but I'm open to exploring alternative methods as well. I attempted to implement t ...

What is the process for creating a widget that can be seamlessly integrated into someone’s website and hosted on your own site

In relation to this previous question. After researching JSONP and conducting some tests, I've realized that I am completely clueless about what I'm doing... What is required? I am developing a customer service tool for people to integrate in ...

What are the steps to integrate mailjet into my Vue application?

I am looking to utilize mailjet for my contact form. I have installed it using "$ yarn add node-mailjet" and followed the steps provided. However, I am a bit confused about whether I am integrating mailjet correctly. Below is the code I am currently using: ...

The Material-ui DatePicker seems to be malfunctioning and as a result, the entire form is not

Struggling to get my DateTimePicker component (could be DatePicker) working after following installation instructions from various sources. I've spent a day and a half attempting to make it functional without success. If you can help me create a separ ...

Clicking on the LI element will automatically trigger a click event on the input radio button

Whenever I click on an li element, I want the corresponding radio input to be selected. However, when I try to do this, I'm seeing an error in the console log: Uncaught RangeError: Maximum call stack size exceeded How can I resolve this issue? Bel ...

What is the best way to substitute multiple characters at a specific position in JavaScript?

I'm struggling to figure out how to replace more than one character at a specific index in a string. I attempted using a FOR loop, but it didn't yield the desired results. String.prototype.replaceAt=function(index, replacement) { return this ...

What is the best way to include the file name and size as query parameters in Node.js?

To retrieve an image from the folder, a query needs to be passed containing the filename and dimensions like this: localhost:3000/images?filename=myImage&width=100&height=100 The initial objective is to fetch images from the designated folder, res ...

Endless Keycloak redirection loop

We have integrated Keycloak 2.3.0.Final into our system and are utilizing the Javascript adapter located at /auth/js/keycloak.js. Our application, built using React+Redux, encounters an issue during the authentication process outlined in the documentation. ...

Save unique data for each tab in the browser

In my web application, I store information about recently visited pages, which I'll refer to as type A. When a user visits a different page type, called B, I display a menu at the top with a button that links back to the most recently visited A-page. ...

When using JavaScript to redirect with window.location, the referrer header is not properly set

Currently, I am attempting to utilize window.location in React to redirect to a third-party page. However, upon making the redirect, the third-party server is not receiving a referrer header from my redirection. Any assistance on resolving this issue wou ...

Error parsing PHP string arrays into JavaScript string arrays

My attempts to convert a name string array from PHP to Javascript have been unsuccessful. var name = <?php echo json_encode($eventname); ?>; and var name = new Array("<?php echo implode('","', $eventName);?>"); I expected the ou ...

Ensuring Smooth Transfer: Sending Local Storage Data to MVC Controller

I'm attempting to send an array of strings from my local storage (key value) to an MVC controller. Here's the code I have so far: Inside the cshtml View file: <script> function getFavouriteBooks() { var ids = JSON.par ...

Angular JS: How to dynamically add and remove checkboxes in ng-repeat?

Currently, I have successfully implemented a Miller column using Angular and Bootstrap. To view the functionality in action, you can check out the code snippet at this link. In the second column of my setup, clicking on a word opens up the third column. ...

Having trouble running the script, chrome error with message passing?

I've hit a roadblock while working on my Chrome extension and could use some assistance. The main issue I'm facing is getting the script to run when activated by the user through an on/off switch in the popup window. It seems like there might be ...

Monitoring and recording user's browsing activities while excluding server-side scripting

Currently, I am working on creating a registration form which will direct users to a "Thank you" page once completed. However, I want to include a button on this confirmation page that will take users back to the previous page they were on before registeri ...