How to assign a function with parameters to click without executing it?

Asked

Viewed 2,096 times

9

Explanation:

I have an application that has by default an event mostraGrupos() and when it is executed, at the end it detaches itself from the element and tries to assign the function escondeGrupos() to the same element.

Code:

function mostraProdutosGrupo(g,elemento) {
  //codigo que mostra os produtos
  elemento.unbind();
  elemento.click(escondeProdutosGrupo(g,elemento)); //aqui ela é atribuida porem executada tambem
}

function escondeProdutosGrupo(g,elemento) {
  //codigo que esconde os produtos
  elemento.unbind();
  elemento.click(mostraProdutosGrupo(g,elemento)); //aqui ela é atribuida porem executada tambem
}

Problem:

When trying to assign the function escondeGrupos() to my element it is assigned but it is also executed at the time of assignment (this cannot happen)

Question:

How to make the function escondeGrupos() is assigned to my element without it being executed at the time of assignment? You can solve this problem?

  • Pass like callback, like this: elemento.click(mostraProdutosGrupo);, without making the call.

  • and the parameters... how they look

  • can for an example in jsFiddle, with the html element code that delegates the first event?

  • From what I understand you want bindar an event to the object and at the same time guardar some parameters to be used later, that’s it?

  • That @Cahe exactly

  • Read about the function bind.

  • You can give an example of your HTML and how you are calling the function mostraProdutosGrupo?

  • This is no satisfactory answer since 2014? Just put function(){mostraProdutosGrupo(g,elemento)} in place of mostraProtudosGrupo(g,elemento). I’ve already welcomed the answer that goes straight to the point, the Rubico, but I’m commenting because it can be hidden among the others.

Show 3 more comments

9 answers

7

After your comments, I would create an event manager, who would be responsible for getting the event (in case of a click), and calling the required function, based on some criteria. This would decrease the complexity of your code.

My basic solution would be like this:

$(seletor).click(function(ev){
    var g = {cookie: "cookie"}
    if(ev.target.my_event){
        ev.target.my_event(g, ev.target);
    }else{
        mostraProdutosGrupo(g, ev.target);
    }
});

And that’s how their job would be:

function mostraProdutosGrupo(g,elemento) {
  //codigo que mostra os produtos
  elemento.my_event = escondeProdutosGrupo;
}

function escondeProdutosGrupo(g,elemento) {
  //codigo que esconde os produtos
  elemento.my_event = mostraProdutosGrupo; 
}

Online example

This is just one of the possible solutions, so I believe you will have fewer problems than keep removing and adding event at all times.

You could also use data-* Attributes to store events.

  • got confused to understand, could not understand the part of my_event what would be the attribute my_event? and when if (ev.target.my_event would give true or false?

  • @Pauloroberto, come on: 1 - The my_event, would be a variable/attribute to store the event in the element. Right? 2 - The if(ev.target.my_event) is for the first click, when the variable/attribute my_event of the element will be undefined, since who attributes the event to her is the mostraProdutosGrupo and escondeProdutosGrupo and at the time of the first click none of them were called. Helped? Have you looked at the jsfiddle example? To better understand you can use the browser Dev Tools and debug the solution.

6

Man, with just a few modifications I got the result I imagine you want.

function mostraProdutosGrupo(g,elemento) {
    console.log('mostra');
    //codigo que mostra os produtos
    elemento.unbind();
    elemento.click(function(){
        escondeProdutosGrupo(g,elemento)
    });
}

function escondeProdutosGrupo(g,elemento) {
    console.log('esconde');
    //codigo que esconde os produtos
    elemento.unbind();
    elemento.click(function(){
        mostraProdutosGrupo(g,elemento)
    });
}

Explanations:

When you cross the line elemento.click(mostraProdutosGrupo(g, Elemento)); you are asking for the function mostraProdutosGrupo is executed and its result (return) is passed as parameter for function click.

Changing to elemento.click(function(){mostraProdutosGrupo(g, Elemento);}); you’re passing a function as parameter and not her call.

IMPORTANT:

Remembering that if you bind your event click by HTML, you will not be able to use unbind.

So don’t use that:

<button id='botao' onclick="mostraProdutosGrupo(event, $(this));">Botao</button>

But rather that:

<button id='botao'>Botao</button>

<script>
    $('#botao').click(function(){
        mostraProdutosGrupo(event, $(this));
    });
</script>

References:

jQuery API . click()

  • 1

    is the answer with cleaner code, recommend as the correct answer

4

I believe it is enough to change the syntax:

function mostraProdutosGrupo(g,elemento) {
    //codigo que mostra os produtos
    elemento.unbind();
    elemento.click(escondeProdutosGrupo); // aqui
}

function escondeProdutosGrupo(g,elemento) {
    //codigo que esconde os produtos
    elemento.unbind();
    elemento.click(mostraProdutosGrupo);// e aqui
}
  • and the parameters as they are?

  • @Pauloroberto, what are these parameters where it comes from?

  • 1

    they come from the function call. when I assign the mostraProdutosGrupo() for the first time in the onload event of the page, elemento is the this that picks up the element itself that clicked, and the g is a value of a cookie

  • in this case you should think about Fernando’s solution, element events should only have parameters relative to the web platform (cursor state or DOM element), or without any parameter, I tried to demonstrate only how not to call the function by passing it as parameter :)

2

I imagine you want to do a "toggle".

function callback1(param1, param2){
  console.log(arguments);
  jQuery('#group').hide();
}

function callback2(param1, param2){
  console.log(arguments);
  jQuery('#group').show();
}


jQuery('#toggle').on('click', function(){
  var _this    = jQuery('#group');
  var visible  = _this.is(':visible');
  if(visible){
    callback1(this, _this);
  }else{
    callback2(this, _this);
  }
});
#group{
  width:150px;
  height:50px;
  border:1px solid #CCC;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="toggle" type="button" value="toggle"/>
<div id="group">
   Produto
</div>

  • If you stick more to the question title and less to the example and function I’m using, I just want to figure out how to bind function with parameter without it running when I do it, only, what the function will do is not relevant.

  • @Pauloroberto See if it fits.

2

I hope that’s it!

I used this HTML file to test:
Note: messages will be shown in the browser console (F12)

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Untitled</title>
        <style>
        li {padding:5px; border-bottom: 1px solid #EEE; cursor:pointer; color:#999;}
        li:hover {border-color: #DDD; color:#000;}
        </style>
    </head>
    <body>


    <ul>
    <li onclick="mostraProdutosGrupo('grupo', this)">Ítem um</li>
    <li onclick="mostraProdutosGrupo('grupo', this)">Ítem dois</li>
    <li onclick="mostraProdutosGrupo('grupo', this)">Ítem tres</li>
    <li onclick="mostraProdutosGrupo('grupo', this)">Ítem quatro</li>
    </ul>

        <script>

        function mostraProdutosGrupo(g,elemento) {
            console.log('mostra: '+elemento.innerHTML);//somente debug - delete me
            setAttOnClick(elemento, "escondeProdutosGrupo('"+g+"', this)");
        }

        function escondeProdutosGrupo(g,elemento) {
            console.log('esconde: '+elemento.innerHTML);//somente debug - delete me
            setAttOnClick(elemento, "mostraProdutosGrupo('"+g+"', this)");
        }

        //Faz um "update" do atributo "onclick"
        function setAttOnClick(e, val){
            var at  = document.createAttribute('onclick');
            at.value = val;
            e.attributes.setNamedItem(at);
        }
        </script>
    </body>
</html>

1

The way I found to add a function at the click of a element, without calling this function when assigned, is adding a Eventlistener.

element.addEventListener(event, function, useCapture)

However to remove the event of the element, the most elegant that I found is by cloning the element, where when cloning, all your Eventlisteners are lost. There is another way to remove, if the function was without paramenters. Follows removeEventListener: http://www.w3schools.com/jsref/met_element_removeeventlistener.asp

The function would look like this:

function mostraProdutosGrupo(g,elemento) {
    //codigo que mostra os produtos
    $("#myBtn").replaceWith($('#myBtn').clone()); 
    document.getElementById("myBtn").addEventListener("click", function() { escondeProdutosGrupo(g,elemento); }); // Aqui é atribuido um novo EventListener.
}

function escondeProdutosGrupo(g,elemento) {
    //codigo que esconde os produtos
    $("#myBtn").replaceWith($('#myBtn').clone());
    document.getElementById("myBtn").addEventListener("click", function() { mostraProdutosGrupo(g,elemento); });
}

1

Firstly, it was not clear enough how the first call of this method is made and where these initial parameters come from. If this information were present, it would be easier to give a more assertive answer.

What I suggest is that you use a class CSS to carry out the change of display status of the products.

Below I worked out an example to show a possible method to solve your problem.

HTML

<div class="elemento">
  <label>Grupo A - <a href="#" data-id-grupo="10">Exibir produtos</a></label>
  <ul class="produtos oculto">
    <li>Elemento 1</li>
    <li>Elemento 2</li>
    <li>Elemento 3</li>
    <li>Elemento 4</li>
  </ul>
</div>

CSS

.produtos.oculto {
  display: none;
}

JS

$('.elemento a').on('click', function()
{
    var $elemento = $(this);
  ///Você pega o ID do grupo ou qualquer informação que desejar do elemento
  var idGrupo = $elemento.data('id-grupo');

    ///Se necessário nesse momento pode-se fazer a
  //requisição ajax dos produtos do grupo, caso
  //eles ainda não tenham sido carregados, ou
  //necessite de atualização

    var $produtos = $elemento.parent().siblings('.produtos');
  $produtos.toggleClass('oculto');

  if ($produtos.hasClass('oculto'))
  {
    $elemento.html('Exibir produtos - Grupo ' + idGrupo);
  }
  else
  {
    $elemento.html('Ocultar produtos - Grupo ' + idGrupo);
  }
});

As there are no more details about its implementation, I put the product group ID as a data-attribute and resgato the same in the click on the link.

If necessary, redeem the products via ajax and then insert into HTML to display.

If the products are already loaded, just use the classes CSS to display/hide products.

Any new consideration I’ll change the example.

If you want he’s available here in this JSFIDDLE to exemplify.

1

This is a very common doubt in Javascript.

How to make the hidden function Groups() be assigned to my element without it being executed at the time of assignment? Has as solve this problem?

Understanding the problem

First, you have to understand why your function is executed at the time of assignment. Let’s go to a simpler example:

function soma(x, y) {
  return x + y;
}

soma(5, soma(3, 2));

When you read the code above, you surely understand that you are not passing the function soma as a parameter for itself, and yes the result of the execution from within. Then, you would read the code above as soma(5, soma(3, 2)) = soma(5, 5) = 10.

I mean, when you’re doing elemento.click(escondeProdutosGrupo(g,elemento));, you are calling the function hidesProducts Group with these parameters in specific, and the result of this function (which in the case is probably a void), you are passing as parameter to the function click jQuery.

There are some ways to resolve this situation:

Option 1: Anonymous function

The most common and simple is you encapsulate the code to be executed in an anonymous function:

elemento.click(function() {
  escondeProdutosGrupo(g, elemento);
});

In that case, you’re moving on to the event click a function that will be executed, and this function will call the escondeProdutosGrupo as you expect it to happen.

Option 2: Function.prototype.bind

From ES5, you have the option to use the method bind which exists in all instances of language function. You pass as the first parameter to this method what you would like it to be the this within that method (pass null if you do not want to change the scope, or if this is not important to your question). And the other parameters will be the parameters that will be passed to the call of that function.

elemento.click(escondeProdutosGrupo.bind(null, g, elemento));

This way, you are passing as parameter the function itself, already loaded with its future parameters, which will be executed when the event click occur.

0

I can recommend two ways that wouldn’t interfere with the readability of your code...

Create a "preparer" function for your events

function mostraProdutosGrupo(g,elemento) {
  //codigo que mostra os produtos
  elemento.unbind();
  elemento.click(prepararEsconderProdutosGrupo(g,elemento)); //aqui ela é atribuida porem executada tambem
}

function escondeProdutosGrupo(g,elemento) {
  //codigo que esconde os produtos
  elemento.unbind();
  elemento.click(prepararMostrarProdutosGrupo(g,elemento)); //aqui ela é atribuida porem executada tambem
}

function prepararMostrarProdutosGrupo(g, element){
     return function(){
         mostraProdutosGrupo(g, element);
     };
}

 function prepararEsconderProdutosGrupo(g, element){
     return function(){
         esconderProdutosGrupo(g, element);
     };
}

Using the . bind function in the functions

another, more current way to approach this is by using the .bind (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)

function mostraProdutosGrupo(g,elemento) {
  //codigo que mostra os produtos
  elemento.unbind();
  elemento.click(escondeProdutosGrupo.bind(this, g,elemento)); //aqui ela é atribuida porem executada tambem
}

function escondeProdutosGrupo(g,elemento) {
  //codigo que esconde os produtos
  elemento.unbind();
  elemento.click(mostraProdutosGrupo.bind(this, g,elemento)); //aqui ela é atribuida porem executada tambem
}

Me personally, in a scenario where it won’t be so common, would use the .bind()to facilitate understanding.

But if you have it too often it’s more interesting to create something to manage your events

Browser other questions tagged

You are not signed in. Login or sign up in order to post.