How to assign a parameterized function to the click without executing it?

9

Explanation:

I have an application that defaults to a mostraGrupos() event and when it is executed, it finally drops off the element and tries to assign the escondeGrupos() function 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 escondeGrupos() function to my element it is assigned but is also executed at the time of assignment (this can not happen)

Question:

How can I make the escondeGrupos() function be assigned to my element without executing it at the time of assignment? Can you solve this problem?

    
asked by anonymous 28.07.2014 / 13:05

9 answers

7

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

My basic solution would look 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 its functions would look like this:

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 sample

  

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

     

You could also use data- * attributes to store the events.

    
28.07.2014 / 13:51
5

Dude, with just a few changes 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 pass the elemento.click(mostraProdutosGrupo(g, Elemento)); line you are asking for the mostraProdutosGrupo function to be executed and its result (return) passed as a parameter to click function.

Changing to elemento.click(function(){mostraProdutosGrupo(g, Elemento);}); you are passing a function as a parameter, not its call.

IMPORTANT:

Remember that if you bind your event click through the HTML, you will not be able to use unbind.

So do not use this:

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

But this:

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

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

References:

jQuery API .click ()

    
22.03.2016 / 09:56
4

I think you just need 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
}
    
28.07.2014 / 13:10
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><inputid="toggle" type="button" value="toggle"/>
<div id="group">
   Produto
</div>
    
18.03.2016 / 16:13
2

Hope this is it!

I used this HTML file to test: Note: messages will be displayed 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>
    
21.03.2016 / 19:51
1

First, 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 CSS class to perform the view state change of the products.

Below is 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);
  }
});

Since there are no more details about your implementation, I've put the product group ID as a data-attribute and redirected it to the click on the link.

If necessary, redeem products via ajax and then enter HTML to display.

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

Any new consideration I'll change by example.

If you want, it's available here at JSFIDDLE for example.

    
18.03.2016 / 20:27
1

The way I found to add a function to click of an element , without calling this function when assigned, is by adding a > EventListener .

element.addEventListener(event, function, useCapture)

But to remove the element event, the elegant I found is cloning the element, where when cloning, all its EventListener is lost. There is another way to remove it if the function is unparamaged. Follow removeEventListener : link

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); });
}
    
21.03.2016 / 20:58
1

This is a very common question in Javascript.

  

How to make the function hideGroups () be assigned to my   without being executed at the time of allocation? Has as   to solve this problem?

Understanding the problem

First, you have to understand why your function should be run at the time of assignment. Let's take a simpler example:

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

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

When you read the code above, you certainly understand that you are not passing the soma function as a parameter to itself, but rather the result of execution from within. So, you would read the above code as soma(5, soma(3, 2)) = soma(5, 5) = 10 .

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

There are a few ways to resolve this:

Option 1: Anonymous function

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

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

In this case, you are passing to the click event a function that will be executed, and this function will call escondeProdutosGrupo as you expect it to happen.

Option 2: Function.prototype.bind

Starting in ES5, you have the option to use the bind method that exists in all language function instances. You pass as the first parameter to this method what you would like to be this within that method (pass null if you do not want to change the scope, or if that is not important for 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));

In this way, you are passing as parameter the function itself, already loaded with your future parameters, which will be executed when the click event occurs.

    
25.03.2016 / 01:22
0

I can recommend two ways that would not disrupt 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 functions

Another, more current way of approaching this is using .bind ( link )

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
}

I particularly, in a scenario where this will not be so common, would use .bind() to make it easier to understand.

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

    
18.03.2016 / 18:57