纪念逝去的头发--一次debug经历

先说前提。

这是极其小几率出现的bug,并不影响一般的网页开发。只是谷歌内核开发的时候才会出现, 基本和前端关系不大。

我们公司有一个pc端的客户端,内嵌了一个谷歌的浏览器。 为了处理某些奇奇怪怪的需求、会按照某些规则屏蔽键盘事件。只有在当前focus可编辑的时候, 才可以使用backspace键这样子, 客户端研发那边使用谷歌的FocusOnEditableField 的API来识别当前的编辑状态

我在使用element重构项目之后,使用了el-select的远程查找、筛选功能,在这里 发现el-select组件全部无法触发FocusOnEditableField了。。。于是开始了dubug之旅。

这个api提到 当前focus的元素如果是可编辑状态,就返回true。这让我想起了dom里面很冷门的一个api

contenteditable 属性规定是否可编辑元素的内容。
复制代码

OK, 来试一下。 这里有几个api可以帮我测试一下

document.activeElement  获取当前文档中获得焦点的元素
dom.contentEditable  获取当前元素的contentEditable值
复制代码
  mounted () {
    document.onkeydown = function (e) {
      console.log(document.activeElement.contentEditable)
    }
  },
复制代码

但是实际打印出来的都是inherit, 看来contentEditable方法也和dom.style一样只能获得行内的值。 翻翻MDN 找到了这个apiisContentEditable 他可以获取计算之后的contentEditable的值。

OK。再来试试

  mounted () {
    document.onkeydown = function (e) {
      console.log(document.activeElement.isContentEditable)
    }
  },
复制代码

在select组建中测试返回false。OK。 我们直接通过修改dom属性的方式来看看是不是这个属性的问题。

  mounted() {
    if (this.$refs.elSelect.$children[0].$options.name === 'ElInput') {
      this.$refs.elSelect.$children[0].$refs.input.contentEditable = true
    }
  },
复制代码

组件加载出来之后,暴力的修改contentEditable属性。。 测试一下, 功能OK, 打完收工~。

当然不可能, 虽然实现了功能,但是有很多疑点啊。 原生的input和elemen库的input都可以正常使用, 应该还有更深层次的问题。。 没办法了, 看源码吧

从package里得知,element通过USE引入的组件都是从\node_modules\element-ui\lib\element-ui.common.js这里引入的。 package里的是未编译过的组件。 开始翻阅源码

然后一无所获。。

编译过的这个文件虽然可以打断点, 但是没有办法去看具体dom的绑定属性。

那就用更简单暴力的方式, 直接从页面上导入element未编译的组建。 node_modules/element-ui/packages/select/src/select.vue就是他了。 然后项目就挂了。这个组件还引入了其他组件, 其中有一部分使用了jsx。 再该环境太麻烦了。直接把和bug无关的代码全部干掉, 项目总算又跑起来了。

这样就清晰多了。。
来看看element的select组建, 涨姿势了。 compositionstart、 compositionupdate、compositionend这三个事件, 新姿势get~。
回归业务,继续找bug。

嗯~~~disabled、readonly这两个属性比较可疑啊。
删掉readonly果然就OK了。 看来问题出现在这里了。

再看看相关的代码

    // 点击父元素的时候
      toggleMenu() {
        if (!this.selectDisabled) {
          if (this.menuVisibleOnFocus) {
            this.menuVisibleOnFocus = false;
          } else {
            this.visible = !this.visible;
          }
          if (this.visible) {
            (this.$refs.input || this.$refs.reference).focus();
          }
        }
      },
复制代码
computed: {
    readonly() {
        // trade-off for IE input readonly problem: https://github.com/ElemeFE/element/issues/10403
        const isIE = !this.$isServer && !isNaN(Number(document.documentMode));
        return !this.filterable || this.multiple || !isIE && !this.visible;
      },
}
复制代码

逻辑就应该是, 点击父元素的时候, 更改visible的值, 然后通过计算属性计算出readonly, 然后通过v-bind来修改dom的属性。

看来找到原因了,看起来这个操作是同步事件, 实际上dom的更新是异步事件。
而谷歌的FocusOnEditableField逻辑应该是, 有元素进入focus->检查该元素的readonly、contentEditable等属性。
而实际上呢。修改了 readonly -> 调用focus事件 这里并不是同步的, input在只读状态进入了focus。 所以无法触发FocusOnEditableField事件了。
具体的可以参考Vue.nextTick方法
写个demoe来试试

    <div style="width: 300px;height: 300px;background-color: pink" @click.stop="toggleMenu">
      <input ref="input" type="text" :readonly="readonly">
    </div>
复制代码
data () {
    return {
      readonly: true
    }
  },
  methods: {
  toggleMenu() {
      this.readonly = false
      console.log(this.$refs.input.readOnly )
    },
  }
复制代码

嗯~果然。第一次点击的时候打印的是true。在改一下

    toggleMenu() {
      this.readonly = false
      this.$nextTick(() => {
        console.log(this.$refs.input.readOnly)
        this.$refs.input.focus()
      })
    },

复制代码

总算找到了问题的原因。

望着桌子上的头发, 这波不亏。

ps: 最近在翻阅element的源码, 收获颇丰。element封装的dispatch方法、broadcast方法也给我以后处理vue组建提供了灵感。感谢大佬们的无私奉献。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值