.hover () and .append () add several elements

advertisements

So I got this code:

$(".l-definition").on({
  mouseenter : function() {
    var definition = $(".l-definition").attr('data-label');
    $(this).append("<div class='definition'>" + definition + "</div>");
    $(".definition").show(300);
  },
  mouseleave: function() {
    $(".definition").hide(300);
  }
});

to append an item on hover using jQuery.

Sometimes it adds that item multiple times, because the cursor hovers the element multiple times accidentaly, which occurs in visual bugs with .show() and .hide().

How can I prevent jQuery from doing that?

Here's a snippet:

$(function() {

  $(".l-definition").on({
    mouseenter: function() {
      var definition = $(".l-definition").attr('data-label');
      $(this).append("<div class='definition'>" + definition + "</div>");
      $(".definition").fadeIn(300);
    },
    mouseleave: function() {
      $(".definition").fadeOut(300);
    }
  });

});
p {
  margin-top: 100px;
}

.definition {
  display: none;
  padding: 5px 10px;
  border-radius: 5px;
  left: 50%;
  top: -70%;
  position: absolute;
  font-size: .8em;
  font-weight: 300;
  background-color: #ededed;
  -webkit-transform: translate(-50%, -100%);
  -moz-transform: translate(-50%, -100%);
  -ms-transform: translate(-50%, -100%);
  -o-transform: translate(-50%, -100%);
  transform: translate(-50%, -100%);
}

.definition::after {
  content: '';
  position: absolute;
  left: calc(50% - 10px);
  top: 100%;
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-top: 10px solid #e8e8e8;
  clear: both;
}

.l-definition {
  font-weight: 600;
  cursor: pointer;
  position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p>Lorem ipsum dolor sit amet consetetur sadipscing <span class="l-definition" data-label="Chromium is a chemical element">Chromium</span></p>

If all you are doing is showing a tooltip, why not use the after pseudo selector, then you will not need any jquery.

I have commented the code below

p {
  margin-top: 100px;
}

.l-definition:after {
  content: attr(data-label);   /* use attr for text */
  padding: 5px 10px;
  border-radius: 5px;
  left: 50%;
  top: -10px;                  /* minus height of arrow */
  position: absolute;
  font-size: .8em;
  font-weight: 300;
  background-color: #ededed;
  opacity:0;                   /* opacity */
  transition: opacity 0.3s;    /* transition opacity over 300ms */
  pointer-events:none;         /* this is so you can mouse things under hidden tooltip */

  -webkit-transform: translate(-50%, -100%);
  -moz-transform: translate(-50%, -100%);
  -ms-transform: translate(-50%, -100%);
  -o-transform: translate(-50%, -100%);
  transform: translate(-50%, -100%);
}

.l-definition::before {
  content: '';
  position: absolute;
  left: calc(50% - 10px);
  bottom: 100%;
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-top: 10px solid #e8e8e8;
  clear: both;
  transition: opacity 0.3s;
  pointer-events:none;
  opacity:0;
}

.l-definition:hover:after,    /* change opacity to 1 when hovered */
.l-definition:hover:before{
   opacity:1;
   pointer-events:auto;
}

.l-definition {
  font-weight: 600;
  cursor: pointer;
  position: relative;
}
<p>Lorem ipsum dolor sit amet consetetur sadipscing <span class="l-definition" data-label="Chromium is a chemical element">Chromium</span></p>

If you want to stick with using jQuery, then I would do it like this:

$(".l-definition").each(function() {
  var label = $(this),
    definition = $('<div class="definition">' + label.data('label') + '</div>');  // cache these objects for better performance

  label.on({
    mouseenter: function() {
      label.append(definition.fadeIn(300));  // append definition and fade in
    },
    mouseleave: function() {
      definition.fadeOut(300, function() {  // fadeout definition and then remove once animation complete
         $(this).remove();
      });
    }
  });
});
p {
  margin-top: 100px;
}

.definition {
  display: none;
  padding: 5px 10px;
  border-radius: 5px;
  left: 50%;
  top: -70%;
  position: absolute;
  font-size: .8em;
  font-weight: 300;
  background-color: #ededed;
  -webkit-transform: translate(-50%, -100%);
  -moz-transform: translate(-50%, -100%);
  -ms-transform: translate(-50%, -100%);
  -o-transform: translate(-50%, -100%);
  transform: translate(-50%, -100%);
}

.definition::after {
  content: '';
  position: absolute;
  left: calc(50% - 10px);
  top: 100%;
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-top: 10px solid #e8e8e8;
  clear: both;
}

.l-definition {
  font-weight: 600;
  cursor: pointer;
  position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p>Lorem ipsum dolor sit amet consetetur sadipscing <span class="l-definition" data-label="Chromium is a chemical element">Chromium</span></p>