1

I'm hoping someone can explain the bind behaviour when passing multiple arguments to a function from an event listener. I haven't been able to find anything that explains it.

I have a button which executes a JS function onclick. I'm trying to bind some parameters to the function when I setup the event listener.

Whenever the function is executed 'CategoryId' comes through correctly, but 'SubheadingText' is coming through as an event object, as is 'event'.

Is there a reason bind does this? Where does the second event object come from?

JS code below

Fiddle is available here: http://jsfiddle.net/8w5geqxj/12/

var faqCategoryButtons = document.getElementsByClassName('button-faq-cat')[0];
var categoryId = 'mycatid';
var subheadingText = 'my cat text';

faqCategoryButtons.addEventListener('click', categoryNavigation.bind(categoryId, subheadingText, event));

function categoryNavigation(categoryId, subheadingText, event){
  if (event && event.preventDefault) {
    event.preventDefault();
  } else if (event) {
    event.returnValue = false;
  }
}

3 Answers 3

1

Bind first parameter always will be the function you are trying to bind "this" object, being the following values assigned to the parameters your function have. If you check the your Fiddle, once the button is clicked you have the following values:

this:           "mycatid",
categoryId:    "my cat text"
subheadingText: Event
event:          MouseEvent

so, in order to make the code work as exepected:

var faqCategoryButtons = document.getElementsByClassName('button-faq-cat')[0];
var categoryId = 'mycatid';
var subheadingText = 'my cat text';

faqCategoryButtons.addEventListener('click', categoryNavigation.bind(null, categoryId, subheadingText, event));

function categoryNavigation(categoryId, subheadingText, event){
  if (event && event.preventDefault) {
    event.preventDefault();
  } else if (event) {
    event.returnValue = false;
  }
}

In this case I have added an extra "null" to make it work because I understand you are not using the "this", however, you can add any value and it will become the "this" of the function.

Edit: reply to the comment's question

It looks like even though when I bind the parameters to the function it needs to be in this order categoryNavigation.bind(null, categoryId, subheadingText, event) But when I define the function, the parameters need to be in this order categoryNavigation(categoryId, subheadingText, context, event){} Is that expected behaviour that the arguments get rearranged when the function is called?

As I mentioned in my answer, the first parameter when you do the binding is the "this", what I would call the context, and that is the real "rearrangement" that the function does. Beside that, the order of the parameters is the same. however, something to take in account is that the "bind" returns a function with the those parameters already "binded" to the old function, and it should be called again.

In your case, you binded null, categoryId, subheadingText, event. However in the shown code, only categoryId and subheadingText were defined, but event was not. That means that the event you binded there, technically should be 'undefined' - but might be a random event value the code had at this point -. In the other hand, click does return its own "event" value as a paramter, so in the end, in your code you have a click event that will call a function that you binded beforehand "null, cathegoryId, subheadingText, ", and that, when the event arises, is called and an the click's event is placed there (but there is not space to place that event, because all the parameters were defined already)

In other words, your code (after the this binded fix)

var faqCategoryButtons = document.getElementsByClassName('button-faq-cat')[0];
var categoryId = 'mycatid';
var subheadingText = 'my cat text';

faqCategoryButtons.addEventListener('click', categoryNavigation.bind(null, categoryId, subheadingText, event));

function categoryNavigation(categoryId, subheadingText, event){
  if (event && event.preventDefault) {
    event.preventDefault();
  } else if (event) {
    event.returnValue = false;
  }
}

is equivalent to:

var faqCategoryButtons = document.getElementsByClassName('button-faq-cat')[0];
var categoryId = 'mycatid';
var subheadingText = 'my cat text';

function categoryNavigationBinded(eventForTheclick){
    const categoryNavigationThis = cagegoryNavigation.bind(null); // in the fixed code, categoryNavigation has its "this = null" 
    categoryNavigationThis(categoryId, subheadingText, event) // note that event is not defined, and for what I know is "undefined"
}

faqCategoryButtons.addEventListener('click', categoryNavigationBinded);

function categoryNavigation(categoryId, subheadingText, event){
  if (event && event.preventDefault) {
    event.preventDefault();
  } else if (event) {
    event.returnValue = false;
  }
}

For the code, I presume you want to do something like this:

var faqCategoryButtons = document.getElementsByClassName('button-faq-cat')[0];
var categoryId = 'mycatid';
var subheadingText = 'my cat text';

function categoryNavigationBinded(eventForTheclick){
    const categoryNavigationThis = cagegoryNavigation.bind(null); // in the fixed code, categoryNavigation has its "this = null" 
    categoryNavigationThis(categoryId, subheadingText, eventForTheclick) // here I am placing the click's event into the 3rd categoryNavigation parameter
}

faqCategoryButtons.addEventListener('click', categoryNavigationBinded);

function categoryNavigation(categoryId, subheadingText, event){
  if (event && event.preventDefault) {
    event.preventDefault();
  } else if (event) {
    event.returnValue = false;
  }
}

That would be the "extended" version, so I can explain what will happen, and the "short version" using the bind in the same way you did in your code would be:

var faqCategoryButtons = document.getElementsByClassName('button-faq-cat')[0];
var categoryId = 'mycatid';
var subheadingText = 'my cat text';

faqCategoryButtons.addEventListener('click', categoryNavigation.bind(null, categoryId, subheadingText)); // the last parameter, "event", will be the parameter the click will place its own event

function categoryNavigation(categoryId, subheadingText, event){
  if (event && event.preventDefault) {
    event.preventDefault();
  } else if (event) {
    event.returnValue = false;
  }
}

TLDR: the parameters doesn't really reorganize in the bind except for the first "this", however in your code, you defined the "event" parameter before you really had the real event value, so it was "lost" (and send as a "5th" parameter when clicking)

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your detailed answer. It looks like even though when I bind the parameters to the function it needs to be in this order categoryNavigation.bind(null, categoryId, subheadingText, event) But when I define the function, the parameters need to be in this order categoryNavigation(categoryId, subheadingText, context, event){} Is that expected behaviour that the arguments get rearranged when the function is called?
sorry, is a bit long to explain here and I cannot show you the example so I will edit my reply to try to resolve your question there properly
1

bind() always receives the context (this) as first argument, thus the other arguments are offset in your case:

faqCategoryButtons.addEventListener('click',
  categoryNavigation.bind(
    null, /* <- context */
    categoryId,
    subheadingText,
    event
  )
);

Comments

0

Start putting arguments, starting at second parameter:

faqCategoryButtons.addEventListener('click', categoryNavigation.bind(null, categoryId, subheadingText, event));

1 Comment

try this is not a problem solving description. what went wrong and what did you do for solving it?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.