CodeMirror有个奇怪的问题,你知道吗?

1,504 阅读4分钟

最近我就在项目中出现了这么个问题,这个问题还不好复现,在本地测了好久才复现出来。

问题复现

在编辑器中,输入 xxx 内容,选中多行(起码两行吧),然后输入要替换的 yyy 内容,最后想要选择就变成了删除,光标在哪就删到哪。

问题如下动画:

选择变成了删除.gif

遇到这个问题,很多人可能跟我的想法一样:要嘛就是输入法有问题,要嘛就是电脑有问题。 不不不,跟着两者都没问题。无论哪种输入法、无论哪台电脑,这个问题都会复现。

本文首发于公众号「前端keep」,欢迎关注。

排查问题

如果出现这类问题,首先先看控制台报错,结果控制台没有任何错误。 接着,翻翻 codemirror 那个组件源码,代码注释过了一遍,调试了一遍发现问题无法定位到具体哪一行代码。

然后,去搜索引擎搜索关于 codemirror 的关键词,根本搜不到。 最后,去 codemirror 的 github 仓库找答案,在仓库中的 issues 中,通过关键词“multiline edit”就可以过滤掉400多条无关的问题。

issue.png

github.com/codemirror/… 这条issue中 nonplus 提了问题:

I have CodeMirror configured with inputStyle="contenteditable" and displaying line numbers and code folding. On the Mac, when using VoiceOver in Chrome, the contents of the gutter (line number and disclosure) is voiced by the screenreader, which confuses users. This happens when the entire page is being read, or when there is a multiline selection inside of the editing area.
FWIW, I do not have this problem in Chrome when using the NVDA screen reader on Windows.
As a workaround, I'm hiding the gutter area from screen readers by setting aria-hidden="true" on .CodeMirror-gutter-wrapper elements, but that feels like a hack.

大致的意思是:

我已经为 CodeMirror 配置了inputStyle="contenteditable",并显示行号和代码折叠。

在 Mac 上,当在 Chrome 中使用 VoiceOver 时,gutter (行号和显示)的内容是由屏幕阅读器发声的, 这使用户感到困惑。当整个页面正在被读取时,或者当编辑区域内有多行选择时,就会发生这种情况。 作为一个解决方案,我通过在 .codemmirror-gutter-wrapper 元素上设置 aria-hidden="true" 来对屏幕阅读器隐藏 gutter 区域,但这感觉像是一种黑客行为。

虽然,他提出的问题跟我的问题不相干,但是从中我敏锐地观察到了一个 api inputStyle

我连忙打开 codemirror 官网,看看 inputStyle 到底是何方神圣。

codemirror.net/doc/manual.…

inputStyle: string
Selects the way CodeMirror handles input and focus. The core library defines the "textarea" and "contenteditable" input models. On mobile browsers, the default is "contenteditable". On desktop browsers, the default is "textarea". Support for IME and screen readers is better in the "contenteditable" model. The intention is to make it the default on modern desktop browsers in the future.

大致的意思是:

选择 comirror 处理输入和焦点的方式。

核心库定义了“textarea”和“contenteditable”输入模型。

在移动浏览器上,默认值是“contenteditable”。

在桌面浏览器中,默认值是“textarea”。

在“contenteditable”模型中,对输入法和屏幕阅读器的支持更好。其目的是让它在未来成为现代桌面浏览器的默认设置。

解决问题

看到这个,我猜测可能就是因为“textarea”的兼容性不太好,换成“contenteditable”试一试。

在 codemirror 组件初始化的时候修改配置:

……省略代码……
this.codeMirrorEditor = CodeMirror.fromTextArea(myTextarea, {
        // mode: ['javascript', 'myMode'], // 自定义模式
        mode: "myMode", // 自定义模式
        lineNumbers: true, // 显示行数
        indentUnit: 4, // 缩进单位为4
        // 加入的api inputStyle
        inputStyle: 'contenteditable',//让文本处于可编辑状态
        styleActiveLine: true, // 当前行背景高亮
        matchBrackets: true, // 括号匹配
        lineWrapping: true, // 自动换行
        theme: "monokai", // 使用monokai模版
        readOnly: false, // 只读
……省略代码……

然后重新测试一番,结果如下动画:

选择正常.gif

从动画中可以看到选择再也不会删除了。

我恍然大悟,原来是因为桌面浏览器中,inputStyle默认值“textarea”搞的鬼。

只要换成兼容性更好的“contenteditable”就好了。

如问题中选择多行进行编辑,输入法瞬间不行,接着选择的内容就被删除。换成“contenteditable”更好的兼容了输出法,并不会造成选择的内容被删掉了。

nice,解决了一个莫名其妙的bug。

总结

这就是我的一个关于 CodeMirror 的奇怪的问题,分享出来给大家。我可以加班,但是必须要让大家早点下班。

遇到问题不要慌,只要问题能复现,多半就可以解决。

解决问题的步骤要明确,不要做无头苍蝇,做无用功的事情。

要学会去看控制台,使用代码调试,学会去看源码。

然后利用好搜索引擎,多半的问题前人已经遇到过,不要自己花费大量的时间去趟一遍。

多看看 github 上的 issues,一般库的作者和使用者就其中问题进行沟通,利用好过滤,搜索对自己有用的内容,对自己解决问题有大大的好处。

要切记,要大胆猜测,小心求证,不要怕选择,不要怕试,就是干。

本文首发于公众号「前端keep」,欢迎关注。

最后,希望大家一定要点赞三连。

更多文章都在我的blog地址