element源码解析(2) -- autocomplete


autocomplete中包含三个组件,input和suggestion/el-scrollbar

在这里插入图片描述
在这里插入图片描述

1.template

一个class:

el-autocomplete:相对定位和行内块;
一个自定义指令:v-clickoutside:点击此dom元素外触发事件

el-input

四个emit触发事件:input,focus,blur,clear调节显示隐藏高亮清除
四个键盘事件up,down,enter,tab
四个插槽:prepend,append,prefix,suffix
v-bind="[$props, $attrs]",v-bind可用数组/对象,说明接收所有跟input相关的属性

el-autocomplete-suggestions

visible-arrow:没见到这个属性发挥作用

四个属性:

:popper-options=“popperOptions”
默认无
:append-to-body=“popperAppendToBody”
默认插入到body

popperAppendToBody: {
        type: Boolean,
        default: true
      },

:placement=“placement”:
默认正下方

 placement: {
        type: String,
        default: 'bottom-start'
      },

:id=“id”
生成id随机数

export const generateId = function(){
  return Math.floor(Math.random() * 10000)
}

popperClass:

属于自定义autocomplete-suggestions的class

2.script

引入防抖函数,外部点击指令,
混入广播派发事件,获取焦点方法Focus(‘input’)
Migrating 生产中没有用处,开发中可以给提示警告

  import debounce from 'throttle-debounce/debounce';
  import Clickoutside from 'element-ui/src/utils/clickoutside';
  import Emitter from 'element-ui/src/mixins/emitter';
  import Migrating from 'element-ui/src/mixins/migrating';
  import { generateId } from 'element-ui/src/utils/util';
  import Focus from 'element-ui/src/mixins/focus';

2.1props

24个属性

  1. valueKey: 默认为value,取list中的key,输入建议对象中用于显示的键名
  2. popperClass:Autocomplete 下拉列表的类名
  3. popperOptions:暂时没用上,应该与vue-popper选项有关
  4. placeholoder: 输入框占位文本
  5. clearable:是否清除,默认不显示
  6. disabled:是否禁用,默认不禁用
  7. name:原生属性,input的name,在v-bind中实现
  8. size:没见着怎么用
  9. value:给默认值
  10. maxlength:最大数量
  11. minlength:最小数量
  12. autofocus:是否自动获取焦点
  13. fetchSuggestions:可以没有queryString,必须回调,回调必须带参数,参数就是suggestions的结果,用意自然是可以去根据queryString去做过滤,但其实直接传过滤后的数据不香吗,绕得不太好.
  14. triggerOnFocus:是否在输入框focus的时候给出建议列表,默认为true,获取焦点的时候去判断
  15. customItem:没发现用到
  16. selectWhenUnmatched:默认为false,enter的时候触发,不匹配,清空
  17. prefixIcon:el-input的属性
  18. suffixIcon:el-input的属性
  19. label:el-input属性
  20. debounce:防抖时间
  21. placement:建议列表位置
  22. hideLoading:是否隐藏loading
  23. popperAppendToBody:弹框是否依附于body,默认依附,否则插入
  24. highlightFirstItem:是否默认突出显示远程搜索建议中的第一项
handleFocus(event) {
        this.activated = true;
        this.$emit('focus', event);
        if (this.triggerOnFocus) {
          this.debouncedGetData(this.value);
        }
      },
props: {
      valueKey: {
        type: String,
        default: 'value'
      },
      popperClass: String,
      popperOptions: Object,
      placeholder: String,
      clearable: {
        type: Boolean,
        default: false
      },
      disabled: Boolean,
      name: String,
      size: String,
      value: String,
      maxlength: Number,
      minlength: Number,
      autofocus: Boolean,
      fetchSuggestions: Function,
      triggerOnFocus: {
        type: Boolean,
        default: true
      },
      customItem: String,
      selectWhenUnmatched: {
        type: Boolean,
        default: false
      },
      prefixIcon: String,
      suffixIcon: String,
      label: String,
      debounce: {
        type: Number,
        default: 300
      },
      placement: {
        type: String,
        default: 'bottom-start'
      },
      hideLoading: Boolean,
      popperAppendToBody: {
        type: Boolean,
        default: true
      },
      highlightFirstItem: {
        type: Boolean,
        default: false
      }
    },

2.2.data

五个自定义变量
actived表示获取焦点时为true,激活搜索建议
suggestions建议列表的数据
loading默认没有加载
highlightedIndex:表示高亮的选项
suggestionDisabled:表示禁用

data() {
      return {
        activated: false,
        suggestions: [],
        loading: false,
        highlightedIndex: -1,
        suggestionDisabled: false
      };
    },

2.3.computed

计算属性:
如果数据发生变化,则根据当前判断是否激活状态,根据是否有数据来显示隐藏:

suggestionVisible() {
  const suggestions = this.suggestions;
  let isValidData = Array.isArray(suggestions) && suggestions.length > 0;
  return (isValidData || this.loading) && this.activated;
},
id() {
   return `el-autocomplete-${generateId()}`;
}

2.4.watch

监听,如果可见性发生变化,直接给子组件派发事件,告诉子组件值和宽度

watch: {
      suggestionVisible(val) {
        let $input = this.getInput();
        if ($input) {
          this.broadcast('ElAutocompleteSuggestions', 'visible', [val, $input.offsetWidth]);
        }
      }
    },

2.5.mounted

this.debouncedGetData = debounce(this.debounce, this.getData);
      this.$on('item-click', item => {
        this.select(item);
      });
      let $input = this.getInput();
      $input.setAttribute('role', 'textbox');
      $input.setAttribute('aria-autocomplete', 'list');
      $input.setAttribute('aria-controls', 'id');
      $input.setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);

2.6.methods

2.6.1:getMigratingConfig开发中的警告

警告

getMigratingConfig() {
        return {
          props: {
            'custom-item': 'custom-item is removed, use scoped slot instead.',
            'props': 'props is removed, use value-key instead.'
          }
        };
      },

2.6.2:getData获取列表数据

获取数据

// 获取数据
      getData(queryString) {
        // 如果禁止建议选项,那么无法继续执行
        if (this.suggestionDisabled) {
          return;
        }
        this.loading = true;
        // 根据参数请求
        this.fetchSuggestions(queryString, (suggestions) => {
          this.loading = false;
          if (this.suggestionDisabled) {
            return;
          }
          // suggestions为数组,将回调函数的参数赋值给列表
          if (Array.isArray(suggestions)) {
            this.suggestions = suggestions;
            this.highlightedIndex = this.highlightFirstItem ? 0 : -1;
          } else {
            console.error('[Element Error][Autocomplete]autocomplete suggestions must be an array');
          }
        });
      },

2.6.3:handleChange,监听input触发

handleChange(value) {
        // 触发input,将input的value传递给父组件
        this.$emit('input', value);
        // 不禁止input选项
        this.suggestionDisabled = false;
        // 如果没有值且不设置获取焦点触发,则设置选项为禁用,将列表置空
        if (!this.triggerOnFocus && !value) {
          this.suggestionDisabled = true;
          this.suggestions = [];
          return;
        }
        // 用防抖重新去获取数据
        this.debouncedGetData(value);
}

2.6.4:handleFocus,监听focus

handleFocus(event) {
        // 获取焦点,激活建议ToolTip
        this.activated = true;
        // 触发获取焦点事件,将事件传递给父元素
        this.$emit('focus', event);
        // 判断是否获取焦点就触发,触发就根据value获取数据
        if (this.triggerOnFocus) {
          this.debouncedGetData(this.value);
        }
}

2.6.5:handleBlur,监听blur

 handleBlur(event) {
 		// 将失焦事件和全部event传递给父组件,
        this.$emit('blur', event);
 }

2.6.6:handleClear,监听清除事件

 handleClear() {
 		// 清除便设置建议列表隐藏
        this.activated = false;
        // 并且将clear事件传递给父组件
        this.$emit('clear');
 }

2.6.7:close,tab按钮触发

close(e) {
        this.activated = false;
      },

2.6.8:handleKeyEnter

handleKeyEnter(e) {
        // 判断建议列表是否显示,判断是否选中建议,判断高亮是否小于长度,阻止默认事件,调用选中方法,如果不匹配,触发select事件,同时置空选中列表
        if (this.suggestionVisible && this.highlightedIndex >= 0 && this.highlightedIndex < this.suggestions.length) {
          e.preventDefault();
          this.select(this.suggestions[this.highlightedIndex]);
        } else if (this.selectWhenUnmatched) {
          this.$emit('select', {value: this.value});
          this.$nextTick(_ => {
            this.suggestions = [];
            this.highlightedIndex = -1;
          });
        }
      },

2.6.9:select enter选中事件

select(item) {
        // 触发input事件,传选中值出来
        this.$emit('input', item[this.valueKey]);
        // 触发select事件,传选中项出来
        this.$emit('select', item);
        // 将当前列表置空
        this.$nextTick(_ => {
          this.suggestions = [];
          this.highlightedIndex = -1;
        });
      },

2.6.10:highlight 高亮

highlight(index) {
        // 不可见,就没有下一步
        if (!this.suggestionVisible || this.loading) { return; }
        // 没有索引值,就没有下一步
        if (index < 0) {
          this.highlightedIndex = -1;
          return;
        }
        // 只要超出,则一直选中最后项
        if (index >= this.suggestions.length) {
          index = this.suggestions.length - 1;
        }
        // 获取下拉窗口
        const suggestion = this.$refs.suggestions.$el.querySelector('.el-autocomplete-suggestion__wrap');
        // 获取所有列表元素
        const suggestionList = suggestion.querySelectorAll('.el-autocomplete-suggestion__list li');
        // 获取选中项
        let highlightItem = suggestionList[index];
        // 滚动距离
        let scrollTop = suggestion.scrollTop;
        // 距离父级高度
        let offsetTop = highlightItem.offsetTop;

        // scrollHeight代表包括当前不可见部分的元素的高度
        if (offsetTop + highlightItem.scrollHeight > (scrollTop + suggestion.clientHeight)) {
          // 比较当前选中项和整个列表项,如果前者大,则将列表的滚动距离 + 选中项距离
          suggestion.scrollTop += highlightItem.scrollHeight;
        }
        // 如果选中项距离顶部高度小于列表框滚动距离,则让列表框的滚动减去选中项的高度
        if (offsetTop < scrollTop) {
          suggestion.scrollTop -= highlightItem.scrollHeight;
        }
        this.highlightedIndex = index;
        let $input = this.getInput();
        $input.setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);
      },
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值