works - onclick javascript




How to make onclick function execute only once? (5)

I have this code for Google analytics on a button. I need it to be executed only once, so that the user can't change statistics by pressing the button many times. I tried solutions from similar topics, but they don't work. Please help. This is my code.

<script>
    function klikaj(i) {
        gtag('event', 'first-4', {
            'event_category' : 'cat-4',
            'event_label' : 'site'
        });
    }

    document.body.addEventListener("click", klikaj(i), {once:true})
</script>


<div id="thumb0" class="thumbs" onclick="klikaj('rad1')">My button</div>

Event Handlers & Listeners

There are three ways* to register an event to an element. The following examples show how to register the click event to a link with the class .once ** which calls the function test() when triggered.

  1. Event Listener (recommended)
    • document.querySelector('.once') .addEventListener('click', test); `
  2. On-event Attribute ( not recommended)
    • <a href='#/' class='once' onclick='test()' >Click</a>
  3. On-event Property
    • document.querySelector('.once') .onclick = test; `

* See DOM on-event handlers for details
** .once class is not relevant for #2

Issues

The OP ( O riginal P ost ) has an event listener (see #1 above) registering a click event to the <body> tag and an on-event attribute (see #2 above) registering the click event to a <div> . Each one calls a function (aka callback function) named klikaj() which is redundant. Clicking the body (which is normally everywhere) isn't useful when you intend to have the user click a div. Should the user click anywhere but the div , klikaj() will be called. Should the user click the div, klikaj() will be called twice. I suggest that you remove both event handlers and replace them with this:

A.

document.getElementById('thumb0').addEventListener("click", klikaj);

Note that klikaj has no parenthesis () because the browser interprets () as to run the function now instead of when the user triggers the registered event (see #1 and #3 above). Should an event handler have additional statements and/or callback functions then an anonymous function should be wrapped around it and normal syntax applies:

B.

document.getElementById('thumb0').addEventListener("click", function(event) { 
  klikaj();
  console.log('clicked');
});

A cleaner alternative is to add extra lines in the definition of the callback function instead and registering events like #A.

Solution

Simply add the following statement as the last line of klikaj() :

this.style.pointerEvents = "none";

That will render the clicked tag unclickable. Applied to OP code it should be like this:

<div id="thumb0" class="thumbs">Thumb 0</div>
<script>
  function klikaj(event) {
    gtag('event', 'first-4', {
      'event_category' : 'cat-4',
      'event_label' : 'site'
    });
    this.style.pointerEvents = "none";   
  }

  document.getElementById('thumb0').addEventListener("click", klikaj);
</script>

Demo

The following demo has two links:

  1. .default - a normal link registered to the click event which when triggered calls test()

  2. .once - a link registered to the click event which when triggered calls test() and renders the link unclickable.

function test() {
  console.log('test');
}

document.querySelector('.default').onclick = function(e) {
  test();
}

document.querySelector('.once').onclick = function(e) {
  test();
  this.style.pointerEvents = 'none';
}
<a href='#/' class='default'>Default</a><br>
<a href='#/' class='once'>Once</a>


I would recommend setting a variable and checking its value.

<script>
    var clicked = false;
    function klikaj(i) {
        if (clicked === false) {
            gtag('event', 'first-4', {
                'event_category' : 'cat-4',
                'event_label' : 'site'
            });
        }
        clicked = true;
    }
    ...
</script>

Or removing the onclick event as suggested by others,

<script>
    function klikaj(i) {
        gtag('event', 'first-4', {
            'event_category' : 'cat-4',
            'event_label' : 'site'
        });
        document.getElementById('thumb0).onclick = undefined;
    }
    ...
</script>

Note that once: true is unfortunately not supported in IE and Edge. See MDN


Just use a flag variable and set it upon the first execution:

var handlerExecuted = false;
function clickHandler() {
  if (!handlerExecuted) {
    console.log("call gtag() here");
    handlerExecuted = true;
  } else {
    console.log("not calling gtag() function");
  }
}
document
  .getElementById("thumb0")
  .addEventListener("click", clickHandler);
<div id="thumb0" class="thumbs">My button</div>

An advance variation that uses closures and could be used on multiple buttons:

function clickHandlerFactory() {
  var handlerExecuted = false;
  return function() {
    if (!handlerExecuted) {
      console.log("call gtag() here");
      handlerExecuted = true;
    } else {
      console.log("not calling gtag() function");
    }
  }
}
[...document.querySelectorAll(".thumbs")].forEach(function(el) {
  el.addEventListener("click", clickHandlerFactory());
});
<div id="thumb0" class="thumbs">Button 1</div>
<div id="thumb1" class="thumbs">Button 2</div>


Remove onclick attribute on your button and register listener via JavaScript, as you tried to do:

<div id="thumb0" class="thumbs"
  style="border: 1px solid; cursor: pointer; float: left">
    My button
</div>
<script>
    function klikaj(i) {
        console.log(i);
    }
    document.getElementById('thumb0')
        .addEventListener("click", function(event) {
            klikaj('rad1');
        }, {once: true});
</script>

If your browser doesn't support { once: true } option, you can remove event listener manually:

<div id="thumb0" class="thumbs"
  style="border: 1px solid;cursor: pointer;float:left">
    My button
</div>

<script>
    function klikaj(i) {
        console.log(i);
    }
    function onClick(event) {
      klikaj('rad1');
      document
        .getElementById('thumb0')
        .removeEventListener("click", onClick);
    }
    
    document
      .getElementById('thumb0')
      .addEventListener("click", onClick);
</script>


you could use removeAttribute() like this: document.getElementById('thumb0').removeAttribute("onclick");

or you could let the function return false like this: document.getElementById('thumb0').onclick = ()=> false





dom-events