閱讀前須知
在本文你將得到以下信息:
- 導(dǎo)致Highcharts圖表寬度無法自適應(yīng)的根本原因
- 解決chart寬度自適應(yīng)的兩種方法
- 如何主動觸發(fā)DOM事件
如有任何疑問或問題,歡迎探討~
問題
今天在實現(xiàn)div元素的sizable的功能是,發(fā)現(xiàn)一個問題:
作為container的div元素寬度發(fā)生變化時,內(nèi)部的Highcharts圖表并沒有自適應(yīng)的變化。然而當(dāng)瀏覽器窗口寬度發(fā)生變化導(dǎo)致container寬度變化時,Highcharts圖表總是可以自適應(yīng)調(diào)整。
分析
官網(wǎng)調(diào)研
由于chart的配置完全相同,問題很可能是Highcharts內(nèi)部的實現(xiàn)導(dǎo)致的。基于這一點,查看Highcharts官方文檔,在Highcharts 響應(yīng)式一節(jié),可以看到:
Highcharts官方文檔提到,默認(rèn)情況下Highcharts圖表都是支持整個圖表跟隨圖表容器響應(yīng)式的,無需額外配置。這一點應(yīng)當(dāng)對應(yīng)著瀏覽器窗口變化導(dǎo)致
container寬度發(fā)生變化時,圖表自適應(yīng)的情況。同時,官方文檔也提到可以調(diào)用reflow()方法來實現(xiàn)chart的自適應(yīng)。盡管官網(wǎng)給出了解決方案,當(dāng)
container寬度發(fā)生變化時調(diào)用reflow()可以解決這個問題,但并沒有給出一個合理的解釋:為什么直接修改container的寬度無法實現(xiàn)圖表的自適應(yīng)。
源碼分析
從官網(wǎng)給出的文檔,可以獲得一個信息:Highcharts本身對chart自適應(yīng)提供了接口reflow(),且默認(rèn)調(diào)用了reflow()方法來支持chart的寬度自適應(yīng)。換句話說,Highcharts源碼中調(diào)用reflow()方法的地方,很可能可以得到導(dǎo)致問題的原因。
基于此,在Highcharts的源碼中,發(fā)現(xiàn)了調(diào)用reflow()的關(guān)鍵方法setReflow():
在紅框中,可以看到
addEvent()的寫法很像addEventListener()的用法,可以合理推測:Highcharts在setReflow()中,調(diào)用了addEventListener()方法,為瀏覽器的window對象添加了一個resize的監(jiān)聽事件,當(dāng)window發(fā)生resize時,會調(diào)用reflow()函數(shù)。這個推測可以合理解釋當(dāng)前的問題:瀏覽器的window變化會觸發(fā)
reflow()方法,而div的resize無法觸發(fā)reflow()。為了驗證這個推測,必須驗證以下兩點:
-
addEvent(win, 'resize', function (e) {})方法是對addEventListener()方法的包裝,且第三個函數(shù)參數(shù)是對事件觸發(fā)時的回調(diào)函數(shù) - 第一個參數(shù)
win是事件的監(jiān)聽者,且win在瀏覽器中就是window對象。
驗證一
根據(jù)對源碼的分析,在源代碼/code/es-modules/parts/Utilities.js中找到了addEvent()的定義:
結(jié)論:推測一正確。
驗證二
基于源碼找到win的定義:
在瀏覽器中,通常
window對象都不是undefined,基于立即執(zhí)行函數(shù)表達(dá)式,可以得到結(jié)論:驗證二正確,第一個參數(shù)win是window對象。
解決方案
基于上述分析,可以得到兩種解決方案:
官網(wǎng)提供的reflow()
當(dāng)container寬度發(fā)生變化時調(diào)用官網(wǎng)提供的reflow(),這種方法直接參考官網(wǎng)文檔,此處不做詳細(xì)介紹。
主動觸發(fā)window的resize事件
根據(jù)對源碼的分析可以知道,Highcharts在window上監(jiān)聽了resize事件,那通過主動觸發(fā)resize事件,應(yīng)當(dāng)同樣可以保證chart的寬度自適應(yīng)。
基于這一出發(fā)點,這里介紹如何 主動創(chuàng)建和觸發(fā) window的resize事件來解決這個問題。
事實上,MDN已給出了文檔,提供了解決方案(以下標(biāo)題可點擊):
這里直接給出答案,在container的寬度發(fā)生變化時調(diào)用以下代碼即可:
const resizeEvent = new Event("resize");
window.dispatchEvent(resizeEvent);
事實證明,這種方法確實可行,問題得到解決!
結(jié)論
找到問題的根本原因是很重要的,至少在下一次,你不會跌倒在同一個坑里。
謝謝閱讀~