代码之家  ›  专栏  ›  技术社区  ›  martinho

使用下拉菜单上的箭头键滚动-vuejs

  •  5
  • martinho  · 技术社区  · 6 年前

    我正在vuejs中构建一个自动完成组件。

    就在那一刻,我陷入了滚动动画。

    其目的是单击箭头键以按键的方向滚动,但滚动仅在选项不可见时执行。

    我想要这种东西,但在VUE/JavaScript中。- http://jsfiddle.net/kMzR9/3/

    如果您在我留下的示例中看不到问题,因为屏幕很小,这里是jsfiddle- https://jsfiddle.net/v7yd94r5/

    这是我的一个例子。

    const Autocomplete = {
      name: "autocomplete",
      template: "#autocomplete",
      props: {
        items: {
          type: Array,
          required: false,
          default: () => []
        },
        isAsync: {
          type: Boolean,
          required: false,
          default: false
        }
      },
    
      data() {
        return {
          isOpen: false,
          results: [],
          search: "",
          isLoading: false,
          arrowCounter: 0
        };
      },
    
      methods: {
        onChange() {
          // Let's warn the parent that a change was made
          this.$emit("input", this.search);
    
          // Is the data given by an outside ajax request?
          if (this.isAsync) {
            this.isLoading = true;
          } else {
            // Let's search our flat array
            this.filterResults();
            this.isOpen = true;
          }
        },
    
        filterResults() {
          // first uncapitalize all the things
          this.results = this.items.filter(item => {
            return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
          });
        },
        setResult(result) {
          this.search = result;
          this.isOpen = false;
        },
        onArrowDown(evt) {
          if (this.arrowCounter < this.results.length) {
            this.arrowCounter = this.arrowCounter + 1;
          }
        },
        onArrowUp() {
          if (this.arrowCounter > 0) {
            this.arrowCounter = this.arrowCounter - 1;
          }
        },
        onEnter() {
          this.search = this.results[this.arrowCounter];
          this.isOpen = false;
          this.arrowCounter = -1;
        },
        showAll() {
          this.isOpen = !this.isOpen;
    			(this.isOpen) ? this.results = this.items : this.results = [];
        },
        handleClickOutside(evt) {
          if (!this.$el.contains(evt.target)) {
            this.isOpen = false;
            this.arrowCounter = -1;
          }
        }
      },
      watch: {
        items: function(val, oldValue) {
          // actually compare them
          if (val.length !== oldValue.length) {
            this.results = val;
            this.isLoading = false;
          }
        }
      },
      mounted() {
        document.addEventListener("click", this.handleClickOutside);
      },
      destroyed() {
        document.removeEventListener("click", this.handleClickOutside);
      }
    };
    
    new Vue({
      el: "#app",
      name: "app",
      components: {
        autocomplete: Autocomplete
      }
    });
    #app {
        font-family: "Avenir", Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        color: #2c3e50;
    }
    
    .autocomplete {
        position: relative;
        width: 130px;
    }
    
    .autocomplete-results {
        padding: 0;
        margin: 0;
        border: 1px solid #eeeeee;
        height: 120px;
        overflow: auto;
        width: 100%;
    }
    
    .autocomplete-result {
        list-style: none;
        text-align: left;
        padding: 4px 2px;
        cursor: pointer;
    }
    
    .autocomplete-result.is-active,
    .autocomplete-result:hover {
        background-color: #4aae9b;
        color: white;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
    <div id="app">
      <autocomplete :items="[ 'Apple', 'Banana', 'Orange', 'Mango', 'Pear', 'Peach', 'Grape', 'Tangerine', 'Pineapple']" />
    
    </div>
    
    <script type="text/x-template" id="autocomplete">
      <div class="autocomplete">
        <input type="text" @input="onChange" v-model="search" @keyup.down="onArrowDown" @keyup.up="onArrowUp" @keyup.enter="onEnter" @click="showAll" />
        <ul id="autocomplete-results" v-show="isOpen" class="autocomplete-results">
          <li class="loading" v-if="isLoading">
            Loading results...
          </li>
          <li v-else v-for="(result, i) in results" :key="i" @click="setResult(result)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }">
            {{ result }}
          </li>
        </ul>
    
      </div>
    </script>
    2 回复  |  直到 6 年前
        1
  •  6
  •   Vanojx1    6 年前

    您需要一个函数来检查当前元素的位置并在需要时移动滚动容器,arrowdown函数也有问题:

    <ul ... ref="scrollContainer" ... >
        ...
        <li ref="options" ... >
        ...
    </ul>
    
    onArrowDown(ev) {
        ev.preventDefault()
        if (this.arrowCounter < this.results.length-1) { <--- HERE NEED -1
            this.arrowCounter = this.arrowCounter + 1;
            this.fixScrolling();
        }
    },
    onArrowUp(ev) {
        ev.preventDefault()
        if (this.arrowCounter > 0) {
            this.arrowCounter = this.arrowCounter - 1;
            this.fixScrolling()
        }
    },
    fixScrolling(){
        const liH = this.$refs.options[this.arrowCounter].clientHeight;
        this.$refs.scrollContainer.scrollTop = liH * this.arrowCounter;
    },
    

    const Autocomplete = {
      name: "autocomplete",
      template: "#autocomplete",
      props: {
        items: {
          type: Array,
          required: false,
          default: () => Array(150).fill().map((_, i) => `Fruit ${i+1}`)
        },
        isAsync: {
          type: Boolean,
          required: false,
          default: false
        }
      },
    
      data() {
        return {
          isOpen: false,
          results: [],
          search: "",
          isLoading: false,
          arrowCounter: 0
        };
      },
    
      methods: {
        onChange() {
          // Let's warn the parent that a change was made
          this.$emit("input", this.search);
    
          // Is the data given by an outside ajax request?
          if (this.isAsync) {
            this.isLoading = true;
          } else {
            // Let's search our flat array
            this.filterResults();
            this.isOpen = true;
          }
        },
    
        filterResults() {
          // first uncapitalize all the things
          this.results = this.items.filter(item => {
            return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
          });
        },
        setResult(result, i) {
          this.arrowCounter = i;
          this.search = result;
          this.isOpen = false;
        },
        onArrowDown(ev) {
          ev.preventDefault()
          if (this.arrowCounter < this.results.length-1) {
            this.arrowCounter = this.arrowCounter + 1;
            this.fixScrolling();
          }
        },
        onArrowUp(ev) {
          ev.preventDefault()
          if (this.arrowCounter > 0) {
            this.arrowCounter = this.arrowCounter - 1;
            this.fixScrolling()
          }
        },
        fixScrolling(){
          const liH = this.$refs.options[this.arrowCounter].clientHeight;
          this.$refs.scrollContainer.scrollTop = liH * this.arrowCounter;
        },
        onEnter() {
          this.search = this.results[this.arrowCounter];
          this.isOpen = false;
          this.arrowCounter = -1;
        },
        showAll() {
          this.isOpen = !this.isOpen;
    			(this.isOpen) ? this.results = this.items : this.results = [];
        },
        handleClickOutside(evt) {
          if (!this.$el.contains(evt.target)) {
            this.isOpen = false;
            this.arrowCounter = -1;
          }
        }
      },
      watch: {
        items: function(val, oldValue) {
          // actually compare them
          if (val.length !== oldValue.length) {
            this.results = val;
            this.isLoading = false;
          }
        }
      },
      mounted() {
        document.addEventListener("click", this.handleClickOutside);
      },
      destroyed() {
        document.removeEventListener("click", this.handleClickOutside);
      }
    };
    
    new Vue({
      el: "#app",
      name: "app",
      components: {
        autocomplete: Autocomplete
      }
    });
    #app {
      font-family: "Avenir", Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      color: #2c3e50;
    }
    
    .autocomplete {
      position: relative;
      width: 130px;
    }
    
    .autocomplete-results {
      padding: 0;
      margin: 0;
      border: 1px solid #eeeeee;
      height: 120px;
      overflow: auto;
      width: 100%;
    }
    
    .autocomplete-result {
      list-style: none;
      text-align: left;
      padding: 4px 2px;
      cursor: pointer;
    }
    
    .autocomplete-result.is-active,
    .autocomplete-result:hover {
      background-color: #4aae9b;
      color: white;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
    <div id="app">
      <autocomplete />
    
    </div>
    
    <script type="text/x-template" id="autocomplete">
      <div class="autocomplete">
        <input type="text" @input="onChange" v-model="search" @keyup.down="onArrowDown" @keyup.up="onArrowUp" @keyup.enter="onEnter" @click="showAll" />
        <ul id="autocomplete-results" v-show="isOpen" ref="scrollContainer" class="autocomplete-results">
          <li class="loading" v-if="isLoading">
            Loading results...
          </li>
          <li ref="options" v-else v-for="(result, i) in results" :key="i" @click="setResult(result, i)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }">
            {{ result }}
          </li>
        </ul>
    
      </div>
    </script>
        2
  •  4
  •   Tiago Matos    5 年前

    我知道它可能很旧,但你可以用 scrollIntoView

    fixScrolling() {
        currentElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
    },