Как убедиться, что событие не сработает снова, если оно еще не остановлено?

Итак, вот в чем дело. На боковой стороне сайта есть строка меню. Когда вы щелкаете по нему, держатель меню откатывается сбоку (меню открывается), когда вы снова нажимаете на панель, держатель меню откатывается назад и скрывает меню. Довольно просто пока. На тат-баре есть значок с текстом "МЕНЮ". Когда вы открываете меню, значок меняется, а текст меняется на "ЗАКРЫТЬ". Когда вы закрываете меню, оно возвращается в нормальное состояние. Все еще не большое дело.

Вот как это выглядит:

закрытое меню

открытое меню (заголовок "ZATVORIŤ" означает "ЗАКРЫТЬ", не беспокойтесь об этом)

Теперь давайте представимся. Нам нужен эффект, текстовый переход, чтобы он выглядел круто и плавно. Поэтому, когда меню закрыто, нет подписи. Там просто значок меню. Текст будет отображаться, когда пользователь наводит курсор на панель. Как только пользователь наводит курсор на строку, надпись "MENU" будет отображаться буква за буквой. Это означает, что первая буква, которая появится, будет "M", вторая "E"... "N"... "U". Весь заголовок появится через несколько сотен миллисекунд, может быть, полсекунды. Когда пользователь уходит, заголовок исчезнет за буквой. Так что "МУЖЧИНЫ"... "Я"... "М"... ". Вы поняли. Заголовок "ЗАКРЫТЬ" работает так же. Когда пользователь нажимает на панель (конечно, ему нужно сначала навести курсор на панель, поэтому уже есть заголовок "МЕНЮ"), заголовок "МЕНЮ" исчезает буква за буквой, а надпись "ЗАКРЫТЬ" будет отображаться буква за буквой. Когда пользователь закрывает меню, надпись "ЗАКРЫТЬ" исчезает таким же образом. Довольно приятная вещь.

Теперь к корню всей проблемы. Это работает, как только пользователь действует нормально. Как только пользователь продолжает слишком быстро наводить курсор на панель, он запускает событие, когда оно уже запущено (я думаю, в чем проблема). То же самое происходит, если он продолжает нажимать на панель - заголовок "ЗАКРЫТЬ" будет испорчен вместо заголовка "МЕНЮ". Затем вместо букв этих заголовков появится пара "неопределенных". Таким образом, заголовок будет выглядеть как "MEundefinedundefinedundefined...." или "MMMMENundefinedundefined..." или что-то подобное. Итак, сначала я подумал, что это ошибка, потому что я сохранил подписи в массивах букв, и он показывает "undefined", верно? Я быстро исправил его, просто чтобы убедиться, что "undefined" не появится - если пользователь попытается остановить его, события просто прекратят запускаться. Он может парить над стойкой, и ничего не произойдет. Он станет нормальным, когда он откроет меню (и закроет его). Но это не так, как это должно работать, и мне нужно это исправить.

Так что мне нужно сделать (я думаю), чтобы убедиться, что событие не сработает, если оно уже происходит (каким-то образом). Я попробовал некоторые функции JavaScript, такие как delay() или setTimeout()... Я пытался сделать функции из эффектов - безуспешно. Я потратил слишком много времени на это, и у меня нет идей и навыков, необходимых для правильной работы. Кто-нибудь может проверить код для меня и дать какое-то предложение или, может быть, исправить его?

ЗДЕСЬ КОД:

HTML:

<div id="navigation_bar"> <!-- THE NAVIGATION BAR WITH ICON AND CAPTION HOLDER FOR THE TEXT -->
    <img id="menu_icon" src="<?= $base_url ?>public/web/images/menu.png">
    <p class="zatvorit">ZATVORIŤ</p>
    <p class="menu">MENU</p>
    <p class="caption_holder"><!-- HERE WILL THE MAGIC HAPPEN --></p> 

</div>
<div id="navigation_holder"> <!-- NOT IMPORTANT PART, ONLY THE MENU HOLDER AND MENU -->

    <p class="close_nav_mobile">
        <img src="<?= base_url() ?>public/web/images/close_menu.png">
    </p>

    <ul id="menu_item_list" class="">
        <li role="presentation"><a class="menu_1" href="<?= $base_url ?>nasa-ponuka">NAŠA PONUKA</a></li>
        <li role="presentation"><a class="menu_2" href="<?= $base_url ?>referencie">REFERENCIE</a></li>
        <li role="presentation"><a class="menu_3" href="<?= $base_url ?>kontakt">KONTAKT</a></li>
        <li id="order_link" class="menu_order_link order_form_open" role="presentation"><a href="#"><b>OBJEDNÁVKA</b></a></li>
        <li id="registration_link" class="menu_reg_form_link reg_form_open" role="presentation"><a href="#">REGISTRÁCIA</a></li>
    </ul>
        <a href="#"><img id="fb_icon" src="<?= $base_url ?>public/web/images/fb_icon.png"></a>
</div>

JavaScript & JQuery (волшебный материал)

<script type="text/javascript">
    menuOn = false; //is true when menu is opened

    //this makes the array of letters for 'MENU' caption
    captionMenu = $( "p.menu" ).text();
    var lettersMenu = [];
    lengthMenu = captionMenu.length;
    for (var i = 0; i <= lengthMenu - 1; i++) {
        lettersMenu[i] = captionMenu.charAt(i);
    };

    //this makes the array of letters for 'CLOSE' (acutally 'ZATVORIŤ') caption
    captionClose = $( "p.zatvorit" ).text();
    var lettersClose = [];
    lengthClose = captionClose.length;
    for (var i = 0; i <= lengthClose - 1; i++) {
        lettersClose[i] = captionClose.charAt(i);
    };

    //some variables to be able to check what's going on
    length = 0; //length of the caption we're working with
    captionMenuOn = false; //true when the 'MENU' caption is visible
    captionCloseOn = false; //true when the 'ZATBORIŤ' caption is visible
    j = 0; // how many letters have appeared already
    k = lengthMenu-1; //example of "how not to name your variables" but It should solve the off-by-one error

    //now the 'MENU' caption will appear letter by letter
    $(document).on("mouseenter", "#navigation_bar", function(){
        if(!menuOn && j==0) { //of course it won't be possible if the menu is opened and there are any letters left
            j = 0;
            length = lengthMenu; //we're working with 'MENU' now
            interval = 150 / length; //counts the interval depending on caption lenght
            for (var i = 0; i <= length - 1; i++) { //looping the letters
                $('p.caption_holder').delay(interval).queue(function (next) {
                    $(this).append( '<span class="caption_parcial_' + j + '">' + lettersMenu[j] + '</span>' );
                    j++; //a letter has appeared
                    if (j == length-1) { //if all the letters have appeared
                        captionMenuOn = true; //now you see me
                    }
                    next();
                });
            }
            k = lengthMenu - 1; //we should have 4 letters there somewhere
        }
    });

    //now the 'MENU' caption will disappear letter by letter an inverse event to that above
    $(document).on("mouseleave", "#navigation_bar", function(){
        if(!menuOn && k==lengthMenu-1) { //so the menu needs to be closed and the 'MENU' caption should be visible
            length = lengthMenu; //we're working with 'MENU' again
            interval = 150 / length;
            k = length;
            for (var i = 0; i <= length - 1; i++) {
                $('.caption_holder').delay(interval / 2).queue(function (next) {
                    k--; //a letter has disappeared
                    $('.caption_parcial_' + k).remove();
                    if (k == 0) { //if we have no letters left
                        captionMenuOn = false; //now you don't (see me)
                    }
                    next();
                });
            }
            j = 0; //we have 0 letters visible now
        }
    });

    //--------------------------------------THIS PART OPENS AND CLOSES THE MENU and resets the 'j' variable in case someone messed up the captions
    $(document).on("click", "#navigation_bar, .close_nav_mobile", function(){
        if(menuOn)
            $(".caption_holder").show();
        smallMenu();
    });

    $(document).on("click", ".menu_order_link", function(){
        smallMenu();
        j=0;
    });
    $(document).on("click", ".menu_reg_form_link", function(){
        smallMenu();
        j=0;
    });

        //function that opens or closes the menu
     function smallMenu() {

      if(!menuOn) { //if the menu is closed

       $("#menu_icon").attr("src", base_url + "public/web/images/close_menu.png").addClass("menu_close"); //icon changes
       //$("nav > ul").show();
            //the 'MENU' caption dissapears, we've seen this code before
          length = lengthMenu; 
          interval = 150 / length;
          j = length;
            for (var i = 0; i <= length - 1; i++) {
                $('.caption_holder').delay(interval / 2).queue(function (next) {
                    j--;
                    $('.caption_parcial_' + j).remove();
                    //and the 'CLOSE' caption will appear right away
                    if (j == 0) {
                        captionMenuOn = false;
                        length = lengthClose; //now we're working with 'CLOSE' caption
                        interval = 150 / length;
                        j = 0;
                        for (var i = 0; i <= length - 1; i++) {
                            $('p.caption_holder').delay(interval).queue(function (next) {
                                $(this).append( '<span class="caption_parcial_' + j + '">' + lettersClose[j] + '</span>' );
                                j++;
                                if (j == length-1) {
                                    captionCloseOn = true;
                                }
                                next();
                            });
                        }
                    }
                    next();
                });
            }



            /*
       $("#navigation_holder").animate(
             {
                 'left' : '0%'
             }, 1250, 'easeOutExpo' );*/

        $("#navigation_holder").css("left", "0%"); //and the menu shows

        menuOn = true; //the menu is opened

      }
      else { //if the menu is opened

       $("#menu_icon").attr("src", base_url + "public/web/images/menu.png").removeClass("menu_close"); //changing icon
            //we need to hide the 'CLOSE' caption
          length = lengthClose; 
          interval = 150 / length;
          j = length;
          for (var i = 0; i <= length - 1; i++) {
              $('.caption_holder').delay(interval / 2).queue(function (next) {
                  j--;
                  $('.caption_parcial_' + j).remove();
                  if (j == 0) {
                      captionCloseOn = false;
                  }
                  next();
              });
          }

       /*
       $("#navigation_holder").animate(
             {
                 'left' : '-50%'
             }, 1250, 'easeOutExpo' );*/

             //and hide the menu
        if($(".mobile_nav").is(":visible")) {
            $("#navigation_holder").css("left", "-100%");
        } else {
            $("#navigation_holder").css("left", "-50%");
        }



       menuOn = false; //now the menu is closed

      }


     }


    //NOT IMPORTANT
     $(window).resize(function() {

        if(!menuOn) {

            if($(".mobile_nav").is(":visible")) {
                $("#navigation_holder").css("left", "-100%");
            } else {
                $("#navigation_holder").css("left", "-50%");
            }

        }

     });


</script>

CSS (если вы хотите правильно запустить его на локальном хосте):

@import url(https://fonts.googleapis.com/css?family=Biryani:400,700,300,600);


* {
    font-family: 'Biryani', sans-serif !important;
    margin: 0px;
    padding: 0px;
}

body, html {
  margin:0;
  padding:0;
  height:100%;
}

body {
  overflow-x:hidden;
}


/*---------------------------NAVIGATION------------------------*/


#navigation_bar {
  width: 55px;
  height: 100%;
  background-color: #000;
  display: block;
  position: fixed;
  margin: 0;
  text-align: center;
  cursor: pointer;
  z-index: 1500;
}

#menu_icon {
  margin: auto;
  max-width: 22px;
  text-align: center;
  position: relative;
  top: 49%;
}

.zatvorit {
  display: none;
  font-size: 16px;
  color: white;
  font-family: "BauerBodoniDOT-Regular", serif !important;
  -ms-transform: rotate(-90deg);
  -webkit-transform: rotate(-90deg);
  transform: rotate(-90deg);
  position: absolute;
top: 50%;
left: -11px;
margin-top: -74px;
}

.menu {
  display: none;
  font-size: 16px;
  color: white;
  font-family: "BauerBodoniDOT-Regular", serif !important;
  -ms-transform: rotate(-90deg);
  -webkit-transform: rotate(-90deg);
  transform: rotate(-90deg);
  position: absolute;
  top: 42%;
  left: 6px;
  margin-top: 5px;
}

.menu_close {
  max-width: 18px !important;
}

.caption_holder {
  font-size: 16px;
  color: white;
  font-family: "BauerBodoniDOT-Regular", serif !important;
  -ms-transform: rotate(-90deg);
  -webkit-transform: rotate(-90deg);
  transform: rotate(-90deg);
  position: absolute;
  top: 50%;
  left: 3px;
  margin-top: -59px;
  width: 50px;
  text-align: left;
}

.right_buttons {
  position: absolute;
  top: 48px;
  right: 55px;
}

.caption_holder span {
  font-family: "BauerBodoniDOT-Regular", serif !important;
  letter-spacing: 3px;
}

#navigation_holder {
  width: 50%;
  max-width: 50%;
  height: 100%;
  background-color: #000;
  display: block;
  position: fixed;
  padding: 50px;
  margin: 0;
  text-align: right;
  z-index: 1000;
  left: -50%;
      transition:all ease 0.5s;
    -webkit-transition:all ease 0.5s;
    -moz-transition:all ease 0.5s;
    -o-transition:all ease 0.5s;
}

СПАСИБО ЗА ПОМОЩЬ И ПРЕДЛОЖЕНИЯ! :)

1 ответ

Использовать шаблон SEMAPHOR с именем переменной locked который false если действие события остановлено (завершено):

var locked_mouseenter=false; //initialize "UNLOCK"
$(document).on("mouseenter", "#navigation_bar", function(){
    if( !locked_mouseenter ){
        locked_mouseenter = true; //lock

        // you code here of event's action

        // code stop here, so the next instruction is : 
        locked_mouseenter = false; //unlock
    }
});
Другие вопросы по тегам