У меня есть три кнопки всплывающей подсказки на странице. Я могу закрыть любую открытую подсказку, нажимая в любом месте за пределами кнопок. И вот что я наткнулся:

В приведенном ниже коде, когда я нажимаю на любое место на странице, активируется обработчик этой части кода $(document).on('click', (event) => this.closeOnOutsideClick(event)); Я вижу в инспекторе, что функция closeOnOutsideClick запускается три раза - она делает три проверки для каждой кнопки всплывающей подсказки, присутствующей на странице. Я не могу понять, какой механизм отвечает за это и почему проверка if (!$(event.target).closest(this.$elem)) не выполняется только один раз? Мой код можно найти здесь, а также ниже: https://jsfiddle.net/bakrall/786cz40L/

Это упрощенная версия более сложного кода, чтобы привести пример моей проблемы:

const selectors = {
  tooltip: '.tooltip-container',
  tooltipButton: '.tooltip-button',
  tooltipMessage: '.tooltip-message'
}

class Tooltip {
  constructor(tooltip) {
    this.$elem = $(tooltip);
    this.$tooltipButton = this.$elem.find(selectors.tooltipButton);
    this.$tooltipMessage = this.$elem.find(selectors.tooltipMessage);
    this.$tooltipMessageText = this.$tooltipButton.attr('data-tooltip-content');

    this.bindUiEvents();
  }

  bindUiEvents() {
    $(document).on('click', (event) => this.closeOnOutsideClick(event));
    this.$tooltipButton.on('click', () => this.showTooltipMessage());
    this.$tooltipButton.on('blur', () => this.hideTooltip());
  }

  showTooltipMessage() {
    this.$tooltipMessage
      .text(this.$tooltipMessageText)
      .addClass('shown-message');
  }

  hideTooltip() {
    this.$tooltipMessage
      .text('')
      .removeClass('shown-message');
  }

  closeOnOutsideClick(event) {
    if (!$(event.target).closest(this.$elem)) {
      this.hideTooltip();
    }
  }
}


//class in another file
const tooltip = $('.tooltip-container');

tooltip.each(function(index, item) {
  new Tooltip(item);
})
.input-wrapper {
  margin-bottom: 2em;
}

.tooltip-container {
  position: relative;
  display: inline-block;
}

.tooltip-message {
  display: none;
  position: absolute;
  left: 100%;
  top: 0;
  width: 10em;
  padding: 0.5rem;
  background: #000;
  color: #fff;
}

.tooltip-message.shown-message {
  display: inline-block;
}

button {
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  border: 0;
  background: #000;
  font-family: serif;
  font-weight: bold;
  color: #fff;
} 

button:focus {
  outline: none;
  box-shadow: 0 0 0 0.25rem skyBlue;
}

input {
  display: block;
}
<!doctype html>
<html class="no-js" lang="">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>	</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="tooltip.css">
    </head>
    <body>
        <div class="input-wrapper">
            <label for="name">
                What's your name?
                <span class="tooltip-container">
                  <button class="tooltip-button" type="button" aria-label="more info"
                    data-tooltip-content="This clarifies whatever needs clarifying">i</button>
                  <span class="tooltip-message" role="status"></span>
                </span>
            </label>
            <input id="name" type="text"/>
        </div>
        <div class="input-wrapper">
            <label for="age">
                What's your age?
                <span class="tooltip-container">
                  <button class="tooltip-button" type="button" aria-label="more info"
                    data-tooltip-content="This is to know how old you are">i</button>
                  <span class="tooltip-message" role="status"></span>
                </span>
            </label>
            <input id="age" type="text"/>
        </div>
        <div class="input-wrapper">
            <label for="nationality">
                What's your nationality
                <span class="tooltip-container">
                  <button class="tooltip-button" type="button" aria-label="more info"
                    data-tooltip-content="What country are you from?">i</button>
                  <span class="tooltip-message" role="status"></span>
                </span>
            </label>
            <input id="nationality" type="text"/>
        </div>
        <script
          src="https://code.jquery.com/jquery-3.4.1.min.js"
          integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
          crossorigin="anonymous">
        </script>
        <script src="tooltip.js" async defer></script>
    </body>
</html>
0
bakrall 17 Авг 2019 в 13:15

2 ответа

Лучший ответ
tooltip.each(function(index, item) {
  new Tooltip(item);
})

Поскольку вы создаете 3 подсказки, вы каждый раз привязываете отдельный прослушиватель событий к документу. Каждый слушатель срабатывает при каждом нажатии. Однако у каждого из этих слушателей есть свой this, который позволяет каждому слушателю сказать, была ли нажата его подсказка, и если нет, скрыть его.

Если вам нужен единственный прослушиватель, вы можете сохранить список всех ваших подсказок и сделать так, чтобы прослушиватель событий перебирал список подсказок, закрывая все подсказки, которые не были нажаты.

1
Omn 17 Авг 2019 в 11:03

Ваше событие нажатия срабатывает на нескольких элементах, потому что вы указали только (документ). Может быть, вы можете быть более конкретным:

$(document).on('click', '.input-wrapper', (event) => this.closeOnOutsideClick(event));
0
kmgt 17 Авг 2019 в 10:27