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

那一刻,我被卷轴动画卡住了.

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

我想要这样的东西,但是用vue/javascript.-http://jsfiddle.net/kMzR9/3/

如果你看不到我留下的例子中的问题,因为屏幕很小,这里是JSFIDLE-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>

推荐答案

您需要一个功能来判断当前元素的位置,并在需要 timeshift 动滚动容器,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>

Vue.js相关问答推荐

在 Vuejs 中更新 Chartjs 图表数据集

vuejs3 youtube-plugin YT 未定义控制台错误

如果在 VueJS/VuetifyJS 中切换switch,则调用函数

如何在 Nativescript 应用程序中调用 axios/api 调用

使用上传组件而不触发 POST 请求

从组件的 内的组件调用方法

Vue 单元测试:您正在开发模式下运行 Vue?

动态插入字符串上的 Vue 事件处理程序不起作用

确保在渲染组件之前加载 Vuex 状态

VueJS + VueRouter:有条件地禁用路由

加载和渲染时对象值上的 Vue.js 未定义错误

如何从 vue.js 中的自定义组件中冒泡点击事件?

如何在 Vue 中动态创建组件上获取更新的 $refs?

如何在 vuetify 中将工具提示添加到数据表标题?

错误:Node Sass 尚不支持您当前的环境:Linux 64-bit with Unsupported runtime (88)

使用 Spring Boot(VueJS 和 Axios 前端)禁止发布 403 帖子

Vue.JS 2.0 修改不相关数据时速度慢

VueJS 2 + ASP.NET MVC 5

v-bind:class 的 Vue.js 计算(computed)属性

Vuex 模块Mutations