简体   繁体   中英

Why does my jquery onclick function toggle on and off, if I run initialization more than once?

I have a website with a few icons/buttons that trigger a simple menu when you click them. However, I need to be able to load more data, with more of these buttons, and since the existing buttons are initialized with a function, using jquery onclick, i figured I would just re-run that function whenever the number of buttons are changed.

I expected the existing onclick functions to be simply overwritten, with the same content as before. Even though it is not optimal code, I expected it to work fast, and simple.

But then I discovered that running the onclick initializing function simply toggles the working elements. In other words the newly generated elements now works, but the existing ones does not. Calling the function a third time toggles them all back, meaning the original works, but not the new. This baffles me.

I made a JSFiddle, showing what I mean:

To see the different scenarios of original state, added button and toggled, and thirdly toggled back, just comment out lines in the document ready function in javascript

$(document).ready(function() {
  initContextMenuButtons(); //initializes cog-icon buttons
  addThirdButton(); //adds a third button, and calls init again, toggling all
  initContextMenuButtons(); //toggles back
});

HTML:

<div id="wrapper">
  <div class="relative-wrapper">
    <i class="fa fa-2x fa-cog context-menu-button"></i>
    <div class="tooltip-wrapper">
      <div class="tooltip">
        <div class="edit-button"><i class="fa fa-fw fa-pencil-square-o" aria-hidden="true"></i>Edit button</div>
        <div class="delete-button"><i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> Delete button</div>
        <div class="close-button"><i class="fa fa-fh fa-times" aria-hidden="true"></i></div>
        <div class="tooltip-arrow-wrapper"><b class="tooltip-arrow"></b></div>
      </div>
    </div>
  </div>
  <div class="relative-wrapper">
    <i class="fa fa-2x fa-cog context-menu-button"></i>
    <div class="tooltip-wrapper">
      <div class="tooltip">
        <div class="edit-button"><i class="fa fa-fw fa-pencil-square-o" aria-hidden="true"></i>Edit button</div>
        <div class="delete-button"><i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> Delete button</div>
        <div class="close-button"><i class="fa fa-fh fa-times" aria-hidden="true"></i></div>
        <div class="tooltip-arrow-wrapper"><b class="tooltip-arrow"></b></div>
      </div>
    </div>
  </div>
</div>

Javascript:

$(document).ready(function() {
  initContextMenuButtons();
  addThirdButton();
  initContextMenuButtons();
});

function initContextMenuButtons() {
  $('.context-menu-button').click(function() {
    var this_wrapper = $(this).siblings(".tooltip-wrapper").toggle();
    $(".tooltip-wrapper").not(this_wrapper).hide();
  });

  $('.tooltip .close-button').click(function() {
    $(this).closest(".tooltip-wrapper").hide();
  });
}

function addThirdButton() {
  var html = ' <div class="relative-wrapper">' +
    '<i class="fa fa-2x fa-cog context-menu-button"></i>' +
    '<div class="tooltip-wrapper">' +
    '<div class="tooltip">' +
    '<div class="edit-button"><i class="fa fa-fw fa-pencil-square-o" aria-hidden="true"></i>Edit button</div>' +
    '<div class="delete-button"><i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> Delete button</div>' +
    '<div class="close-button"><i class="fa fa-fh fa-times" aria-hidden="true"></i></div>' +
    '<div class="tooltip-arrow-wrapper"><b class="tooltip-arrow"></b></div>' +
    '</div>' +
    '</div>' +
    '</div>'
  $('#wrapper').append(html);
  initContextMenuButtons();
}

CSS:

.relative-wrapper {
  position: relative;
}

.tooltip .close-button {
  position: absolute;
  top: 0px;
  right: 1px;
  color: #999;
}

.tooltip .close-button:hover {
  color: #555;
}

.tooltip-wrapper {
  display: none;
  position: absolute;
  top: -6px;
  left: 38px;
  z-index: 1;
}

.tooltip {
  transition: none 0s ease 0s;
  width: 200px;
  height: 55px;
  background-color: rgb(246, 246, 246);
  border: 1px solid #ccc;
  border-radius: 4px;
  position: relative;
  box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px 0px;
}

.tooltip .edit-button {
  position: absolute;
  top: 7px;
  left: 8px;
  width: 185px;
  cursor: pointer;
}

.milestone-tooltip .edit-button:hover {
  background-color: #ddd;
}

.tooltip .delete-button {
  position: absolute;
  top: 28px;
  left: 6px;
  width: 185px;
  cursor: pointer;
}

.tooltip .delete-button:hover {
  background-color: #ddd;
}

.tooltip-arrow-wrapper {
  height: 24.8px;
  width: 16px;
  display: block;
  transition: none 0s ease 0s;
  left: -14px;
  top: 11px;
  position: absolute;
}

.tooltip-arrow {
  height: 10px;
  width: 10px;
  border-top: 1px solid rgb(187, 187, 187);
  border-left: 1px solid rgb(187, 187, 187);
  background-color: rgb(246, 246, 246);
  transform: translate(8px, 4px) rotate(-45deg);
  position: absolute;
}

.context-menu-button {
  cursor: pointer;
}

Your event listeners add up. To make multiple initializations work, you can remove existing ones first.

$('.context-menu-button').off().click(function() {
// ...
$('.tooltip .close-button').off().click(function() {

In general, you can also use namespaces to prevent accidentally removing other event listeners.

$('.context-menu-button').off('.init').on('click.init', function() {
// ...
$('.tooltip .close-button').off('.init').on('click.init', function() {

Existing event handlers don't get overwritten. The new event handlers simply stack on top of them. One click now fires the event multiple times.

I'd highly recomment using Event Delegation for the following reasons:

  • Dynamically created elements will just work . No need to attach new events.

  • With your current method, hypothetically, if you have 10 elements, that's 10 event handlers. Event delegation allows you to have just 1 , whether you have 1 element or 1,000.

  • It's nearly identical to your existing code. You're simply attaching the event to the document instead of the element itself.

function initContextMenuButtons() {
  $(document).on('click', '.context-menu-button', function() {
    var this_wrapper = $(this).siblings(".tooltip-wrapper").toggle();
    $(".tooltip-wrapper").not(this_wrapper).hide();
  });

  $(document).on('click', '.tooltip .close-button', function() {
    $(this).closest(".tooltip-wrapper").hide();
  });
}

initContextMenuButtons() should now only be fired once throughout the life of the page, so be sure to remove the call from your addThirdButton() method, as well as the second call from your $(document).ready( ... ) .

SOLUTION:

Remove initContextMenuButtons() function and its references. Add the following code.

 $('#wrapper').on('click', '.context-menu-button', function () {
  var this_wrapper = $(this).siblings(".tooltip-wrapper").toggle();
      $(".tooltip-wrapper").not(this_wrapper).hide();
});

$('#wrapper').on('click', '.tooltip .close-button', function() {
    $(this).closest(".tooltip-wrapper").hide();
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM