高能操作
高亮变动的内容
诉求:在json编辑器中进行了内容的更新,期望能在简历中高亮展示出变动的内容
转为技术需求就是期望能监听到变动的dom,然后高亮
这个地方就用到 MutationObserver了
它提供了监视对DOM树所做更改的能力
/** * 高亮变化的Dom */ function initObserver() { // 包含子孙节点 // 将监视范围扩展至目标节点整个节点树中的所有节点 // 监视指定目标节点或子节点树中节点所包含的字符数据的变化 const config = { childList: true, subtree: true, characterData: true }; // 实例化监听器对象 const observer = new MutationObserver(debounce(function (mutationsList, observer) { for (const e of mutationsList) { let target = e.target if (e.type === 'characterData') { target = e.target.parentElement } // 高亮 highLightDom(target) } }, 100)) // 监听子页面的body observer.observe(document.getElementById('page').contentDocument.body, config); // 因为 MutationObserver 是微任务,微任务后面紧接着就是页面渲染 // 停止观察变动 // 这里使用宏任务,确保此轮Event loop结束 setTimeout(() => { observer.disconnect() }, 0) } function highLightDom(dom, time = 500, color = '#fff566') { if (!dom?.style) return if (time === 0) { dom.style.backgroundColor = '' return } dom.style.backgroundColor = '#fff566' setTimeout(() => { dom.style.backgroundColor = '' }, time) }
何时调用 initObserver
当然是在更新页面之前的时候注册事件,页面完成变动渲染后停止监听
function updatePage(data) { // 异步的微任务,本轮event loop结束停止观察 initObserver() // 同步 setSchema(data, getPageKey()) // 同步 + 渲染页面 refreshIframePage() }
效果
网络异常,图片无法展示
|
点哪改哪
期望效果
网络异常,图片无法展示
|
诉求:
- 点击需要修改的部分,就能进行修改操作
- 修改结果在简历上与json编辑器中进行内容同步
下面阐述一下实现
1. 获取点击的Dom
document.getElementById('page').contentDocument.body.addEventListener('click', function (e) { const $target = e.target })
2. 获取dom内容在页面中出现的次数与相对位置
- 子页面只包含展示逻辑,所以需要父页面做hack操作才能在定位点击内容在json中对应位置
- 拥有相同内容的dom不止一个,所以需要全部找出来
/** * 遍历目标Dom树,找出文本内容与目标一致的dom组 */ function traverseDomTreeMatchStr(dom, str, res = []) { // 如果有子节点则继续遍历子节点 if (dom?.children?.length > 0) { for (const d of dom.children) { traverseDomTreeMatchStr(d, str, res) } // 相等则记录下来 } else if (dom?.textContent?.trim() === str) { res.push(dom) } return res } // 监听简历页的点击事件 document.getElementById('page').contentDocument.body.addEventListener('click', function (e) { const $target = e.target // 点击的内容 const clickText = $target.textContent.trim() // 只包含点击内容的节点 const matchDoms = traverseDomTreeMatchStr(document.getElementById('page').contentDocument.body, clickText) // 点击的节点在 匹配的 节点中的相对位置 const mathIndex = matchDoms.findIndex(v => v === $target) // 不包含则不做处理 if (mathIndex < 0) { return } })
3. 获取jsoneditor中对应的节点
- 与上面逻辑类似
- 先过滤出只包含此节点内容的几个节点
- 然后根据点击dom在同内容节点列表中的相对位置进行匹配
// 监听简历页的点击事件 document.getElementById('page').contentDocument.body.addEventListener('click', function (e) { // ...省略上述列出的代码 // 解除上次点击的dom高亮 highLightDom($textarea.clickDom, 0) // 高亮这次的10s highLightDom($target, 10000) // 更新jsoneditor中的search内容 editor.searchBox.dom.search.value = clickText // 主动触发搜索 editor.searchBox.dom.search.dispatchEvent(new Event('change')) // 将点击内容显示在textarea中 $textarea.value = clickText // 自动聚焦输入框 if (document.getElementById('focus').checked) { $textarea.focus() } // 记录点击的dom,挂载$textarea上 $textarea.clickDom = e.target // jsoneditor 搜索过滤的内容为模糊匹配,比如搜索 a 会匹配 ba,baba,a,aa,aaa // 根据上面得到的matchIndex,进行精确匹配全等的json节点 let i = -1 for (const r of editor.searchBox.results) { // 全等得时候下标才变动 if (r.node.value === clickText) { i++ // 匹配到json中的节点 if (i === mathIndex) { // 高亮一下$textarea $textarea.style.boxShadow = '0 0 1rem yellow' setTimeout(() => { $textarea.style.boxShadow = '' }, 200) return } } // 手动触发jsoneditor的next search match 按钮, 切换jsoneditor中active的节点 editor.searchBox.dom.input.querySelector('.jsoneditor-next').dispatchEvent(new Event('click')) // active的节点可以通过下面方式获取 // editor.searchBox.activeResult.node } })
4. 更新节点内容
- 上面两个步骤将简历中的dom与jsoneditor的dom都获取到了
- 通过textarea输入的内容
- 将输入的内容分别更新到这两个dom上,并把最新的json写入的localStorage中
// 监听输入事件,并做一个简单的防抖 $textarea.addEventListener('input', debounce(function () { if (!editor.searchBox?.activeResult?.node) { return } // 激活dom变动事件 initObserver() // 更新点击dom $textarea.clickDom.textContent = this.value // 更新editor的dom editor.searchBox.activeResult.node.value = this.value editor.refresh() // 更新到本地 setSchema(editor.get(), getPageKey()) }, 100))
这样就完成了两侧(简历/jsoneditor)数据的更新
后续规划
- 接入更多的框架支持
- 优化pdf的导出
- 超链接
- 字体图标
- 优化用户体验
- 降低jsoneditor的存在感,当前的新增与删除操作依赖jsoneditor,对不懂前端魔法的同学不友好
- 优化移动端的交互
- 美化界面
- 加入自动生成代码模板指令
- 搬运更多的简历模板
感谢你能坚持读到这里,谢谢捧场,如果你也感兴趣欢迎贡献代码(简历/功能)