资源
花了 将近两天时间 几天时间, 边构思, 边实践, 实现了一个使用 highlight.js 高亮页面中的代码片段的油猴脚本.
一开始的逻辑十分简单, 直接遍历页面中的 pre code
节点, 然后使用 highlight.js 高亮即可.
后来又不仅想高亮, 还想要换字体, 换主题, 随需求增长所带来的复杂度已经足够写一个 UI 界面来配置这些选项.
于是问题就来了, 该如何使得 UI 界面注入到宿主页面中并不受宿主页面的影响?
shadow DOM
早就听闻 shadow DOM 各种神奇, 一试之下果然非常给力.
shadow DOM 最引人注目的特性是可以隔离宿主样式作用域, 另辟天地.
正得益于此, 我们只需要创建 HTML 和 Body 元素挂载到 Shadow DOM 上,
我们的页面就像是在新的页面上打开的一样.
节选一段 Syntax highlighting 相关源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| let parasitifer = null const openSetting = () => { if (parasitifer) { parasitifer.show() return true }
parasitifer = document.createElement('div') parasitifer.id = 'host-element' parasitifer.style = `position: fixed;top:0;bottom:0;z-index:9999;width:100vw;height:100vh;font-size:16px;background-color:#fff;` parasitifer.show = () => { parasitifer.style.display = 'block' } parasitifer.hide = () => { parasitifer.style.display = 'none' }
const shadowRoot = parasitifer.attachShadow({ mode: 'open' }) const shadowContent = document.createElement('HTML') const shadowStyleEle = document.createElement('style') const bulmaStyleEle = document.createElement('style') const fontStyleEle = document.createElement('style') const themeStyleEle = document.createElement('style') const vueContainer = document.createElement('div') vueContainer.id = 'vue-root' shadowStyleEle.innerText = `` shadowContent.appendChild(shadowStyleEle) shadowContent.appendChild(bulmaStyleEle) shadowContent.appendChild(fontStyleEle) shadowContent.appendChild(themeStyleEle) shadowContent.appendChild(vueContainer) shadowRoot.appendChild(shadowContent) document.body.appendChild(parasitifer)
const mount = style => { bulmaStyleEle.innerText = style
const vueRoot = document .querySelector('#host-element') .shadowRoot.querySelector('#vue-root') const vueTemplate = `<body style="height: 100vh"> <div id="app-vue" class="container"> Other DOM </div> </body>` const vm = new window.Vue({ el: vueRoot, template: vueTemplate, data () { return { } }, watch: { }, methods: { }, mounted () { } }) }
|
其中值得特别注意是 vue
的 el
选项.