阅读前须知
在本文你将得到以下信息:
导致 Highcharts 图表宽度无法自适应的根本原因
解决 chart 宽度自适应的两种方法
如何主动触发 DOM 事件
如有任何疑问或问题, 欢迎探讨~
问题
今天在实现 div 元素的 sizable 的功能是, 发现一个问题:
作为 container 的 div 元素宽度发生变化时, 内部的 Highcharts 图表并没有自适应的变化. 然而当浏览器窗口宽度发生变化导致 container 宽度变化时, Highcharts 图表总是可以自适应调整.
分析
官网调研
由于 chart 的配置完全相同, 问题很可能是 Highcharts 内部的实现导致的. 基于这一点, 查看 Highcharts 官方文档, 在 Highcharts 响应式一节, 可以看到:
Highcharts 响应式
Highcharts 官方文档提到, 默认情况下 Highcharts 图表都是支持整个图表跟随图表容器响应式的, 无需额外配置. 这一点应当对应着浏览器窗口变化导致 container 宽度发生变化时, 图表自适应的情况. 同时, 官方文档也提到可以调用 reflow() 方法来实现 chart 的自适应.
尽管官网给出了解决方案, 当 container 宽度发生变化时调用 reflow() 可以解决这个问题, 但并没有给出一个合理的解释: 为什么直接修改 container 的宽度无法实现图表的自适应.
源码分析
从官网给出的文档, 可以获得一个信息: Highcharts 本身对 chart 自适应提供了接口 reflow(), 且默认调用了 reflow() 方法来支持 chart 的宽度自适应. 换句话说, Highcharts 源码中调用 reflow() 方法的地方, 很可能可以得到导致问题的原因.
基于此, 在 Highcharts 的源码中, 发现了调用 reflow() 的关键方法 setReflow():
setReflow()
在红框中, 可以看到 addEvent() 的写法很像 addEventListener() 的用法, 可以合理推测: Highcharts 在 setReflow() 中, 调用了 addEventListener() 方法, 为浏览器的 Windows 对象添加了一个 resize 的监听事件, 当 Windows 发生 resize 时, 会调用 reflow() 函数.
这个推测可以合理解释当前的问题: 浏览器的 Windows 变化会触发 reflow() 方法, 而 div 的 resize 无法触发 reflow().
为了验证这个推测, 必须验证以下两点:
addEvent(win, 'resize', function (e) {})
方法是对 addEventListener() 方法的包装, 且第三个函数参数是对事件触发时的回调函数
第一个参数 win 是事件的监听者, 且 win 在浏览器中就是 Windows 对象.
验证一
根据对源码的分析, 在源代码 / code/es-modules/parts/Utilities.JS 中找到了 addEvent() 的定义:
addEvent()
结论: 推测一正确.
验证二
基于源码找到 win 的定义:
win 的定义
在浏览器中, 通常 Windows 对象都不是 undefined, 基于立即执行函数表达式, 可以得到结论: 验证二正确, 第一个参数 win 是 Windows 对象.
解决方案
基于上述分析, 可以得到两种解决方案:
官网提供的 reflow()
当 container 宽度发生变化时调用官网提供的 reflow(), 这种方法直接参考官网文档, 此处不做详细介绍.
主动触发 Windows 的 resize 事件
根据对源码的分析可以知道, Highcharts 在 Windows 上监听了 resize 事件, 那通过主动触发 resize 事件, 应当同样可以保证 chart 的宽度自适应.
基于这一出发点, 这里介绍如何 主动创建和触发 Windows 的 resize 事件来解决这个问题.
事实上, MDN 已给出了文档, 提供了解决方案 (以下标题可点击):
创建和触发 events
Event()
这里直接给出答案, 在 container 的宽度发生变化时调用以下代码即可:
- const resizeEvent = new Event("resize");
- Windows.dispatchEvent(resizeEvent);
事实证明, 这种方法确实可行, 问题得到解决!
结论
找到问题的根本原因是很重要的, 至少在下一次, 你不会跌倒在同一个坑里.
谢谢阅读~
来源: http://www.jianshu.com/p/f749faef8cf7