Пользовательский выпадающий Vue сохранить фокус - PullRequest
0 голосов
/ 21 сентября 2018

Я пытался найти решение здесь для этого, но не смог и решил написать вопрос.

Поэтому я пытаюсь создать простой компонент раскрывающегося меню в Vue, в котором есть кнопка переключения ипользователь может свободно перемещаться и перемещаться по элементам, но как только фокус покидает компонент, меню должно свернуться.

Теперь я сохранил фокус с событиями @blur и @focus, ноУ меня проблема с кнопкой Toggle.Если я присоединяю к нему тех же слушателей, то щелчок по нему показывает и сразу же скрывает меню, поэтому вам нужно снова щелкнуть, чтобы развернуть его.

Вот скрипка , демонстрирующая проблему.

Если, однако, я удаляю слушателей, то нажать кнопку после того, как фокус был внутри компонента, проблематично.Я предполагаю, что мой подход неверен, поэтому вот ожидаемое поведение:

  • Кнопка Toggle - это просто кнопка переключения .Он должен свернуть / развернуть список по щелчкам
  • Список можно расширить только нажатием кнопки Toggle, когда он закрыт
  • Список свернут эфир, нажав кнопку Toggleкогда он расширяется или фокусируется на нем.Это включает в себя фокусировку на кнопке (то есть нажатие на кнопку-переключатель, чтобы развернуть список, а затем щелкнуть где-нибудь снаружи, не фокусируя ни один из элементов).

РЕДАКТИРОВАТЬ : Благодаря @Sphinx мне удалось заставить раскрывающийся список работать, как я ожидаю.Вот обновленная скрипка .

Ответы [ 4 ]

0 голосов
/ 21 сентября 2018

Для вашего случая, как уже отмечалось в комментариях к вопросу, вы должны обрабатывать множество ситуаций, например, @focus и @click будут вызваны подряд, @blur из <button> и @blurиз <ul></li> будет срабатывать при нажатии на любой из них.

Это не очень хорошая идея .Но вы можете проверить Эта скрипка , это одно решение с setTimeout & clearTimeout.Тогда вы, возможно, уже видели, что он задерживается на 100 мсек setTimeout(()=>{}, 100) (я добавил несколько журналов, вы можете открыть консоль браузера, чтобы проверить рабочий процесс). Причина в том, что нам нужно подождать достаточно времени , чтобы убедиться, что следующий обработчик событий (например, сначала сработал фокус, затем будет активирован щелчок) может очистить предыдущий setTimeout во время , если только меню не может быть открыто сначала, а затем снова закрыто.(PS: для некоторых старых машин 100 мс может быть недостаточно, это зависит от того, как быстро закончится текущий рендеринг)

Одно решение :

  1. удалить @focus и @blur

  2. , когда this.showMenu истинно (открыто), добавить один слушатель = click для Dom = документа, он будет выполняться this.hide() при запуске.

  3. Затем внутри this.hide() удалите этот прослушиватель = click из Dom = документа.

  4. , чтобы предотвратить свертывание меню принажмите на кнопку и в меню, добавьте модификатор = stop, он остановит распространение события щелчка на узлы Dom верхнего уровня.

Если вы оберните <button> и <ul> водин <div>, тогда нужно только добавить модификатор = stop вроде <template><div @click.stop><button></button<ul>...<ul></div></template>.

Ниже одно демо :

Vue.config.productionTip = false

new Vue({
  el: "#app",
  data: {
    showMenu: false,
    items: ['Option 1', 'Option 2', 'Option 3', 'Option 4']
  },
  computed: {
    listClass: function() {
      if (this.showMenu) {
        return 'show';
      }
      return '';
    }
  },
  methods: {
    toggle: function() {
      this.showMenu = !this.showMenu
      this.showMenu && this.$nextTick(() => {
        document.addEventListener('click', this.hide)
      })
    },
    hide: function() {
      this.showMenu = false
      document.removeEventListener('click', this.hide)
    }
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

ul {
  list-style: none;
  display: none;
}

li {
  padding: 5px;
  border: 1px solid #000;
}

li:hover {
  cursor: pointer;
  background: #aaa;
}

.show {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <h3>
    This is one demo:
  </h3>
  <button @click.stop="toggle" tabindex="0">
    Toogle menu
  </button>
  <ul :class="listClass" @click.stop>
    <li v-for="(item, key) in items" tabindex="0">{{ item }}</li>
  </ul>
</div>
0 голосов
/ 21 сентября 2018

Я примерно на 40% уверен, что понял проблему ...

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

шаблон:

<div id="app">
  <button 
    @click="showMenu" 
    tabindex="0"
    @focus="itemFocus"
    @blur="itemBlur"
    >
    Toogle menu
  </button>
  <ul :class="listClass">
    <li
      v-for="(item, key) in items"
      tabindex="0"
      @focus="itemFocus"
      @blur="itemBlur"
    >{{ item }}</li>
  </ul>
</div>

метод:

showMenu: function(){
    this.showMenu = true;
},

это откроет меню, когда оно закрыто, но не закроет его

https://jsfiddle.net/wc1oehx9/

0 голосов
/ 21 сентября 2018

Уловка с этими типами пользовательского интерфейса с несколькими способами взаимодействия состоит в том, чтобы установить состояние элемента пользовательского интерфейса и заставить элемент пользовательского интерфейса отражать это состояние.

Итак, что я сделал, это было:

  • Если мышь входит, меню должно быть открыто
  • Если мышь уходит, меню должно быть закрыто
  • Если оно сфокусировано, меню должно быть открыто
  • Если оно размыто, меню должно быть закрыто

Итак, вот мое решение: https://jsfiddle.net/jaiko86/e0vtf2k1/1/

HTML:

<div id="app">
  <button 
    tabindex="0"
    @focus="isMenuOpen = true"
    @blur="isMenuOpen = false"
    @mouseenter="isMenuOpen = true"
    @mouseout="isMenuOpen = false"
    >
    Toogle menu
  </button>
  <ul :class="listClass">
    <li
      v-for="(item, key) in items"
      tabindex="0"
      @focus="isMenuOpen = true"
      @blur="isMenuOpen = false"
      @mouseenter="isMenuOpen = true"
      @mouseout="isMenuOpen = false"
    >{{ item }}</li>
  </ul>
</div>

JS:

// simplified for clarity
new Vue({
  el: "#app",
  data: {
    showMenu: false,
    items: [ 'Option 1', 'Option 2', 'Option 3', 'Option 4' ],
    isMenuOpen: false,
  },
  computed: {
    listClass() { // you can omit the `: function` in new ES standard
      return this.isMenuOpen ? 'show' : ''; //ternary op saves lines
  },
  methods: {
    // not needed, as it's done in HTML
    // toggle: function(){
    //  this.showMenu = !this.showMenu
    //},
    /*
    we no longer need these methods:
    itemFocus: function() {
        var self = this;
      Vue.nextTick(function() {
        if(!self.showMenu) {
            self.showMenu = true;
        }
      });
    },
    itemBlur: function() {
        var self = this;
            Vue.nextTick(function() {
        if(self.showMenu) {
            self.showMenu = false;
        }
      });
    }
    */
  }
})
0 голосов
/ 21 сентября 2018

Я не уверен, что это исправляет то, что вы пытаетесь достичь, но вы можете попробовать использовать @mouseenter и @ mouseout

<button 
@click="toggle" 
tabindex="0"
@mouseenter="itemFocus"
@mouseout="itemBlur"
>
Toogle menu

Вот скрипка: https://jsfiddle.net/xhmsf9yw/5/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...