我打造了一个简历在线生成应用(三)

简介: 我打造了一个简历在线生成应用(三)

高能操作


高亮变动的内容


诉求:在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内容在页面中出现的次数与相对位置


  1. 子页面只包含展示逻辑,所以需要父页面做hack操作才能在定位点击内容在json中对应位置
  2. 拥有相同内容的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. 更新节点内容


  1. 上面两个步骤将简历中的dom与jsoneditor的dom都获取到了
  2. 通过textarea输入的内容
  3. 将输入的内容分别更新到这两个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)数据的更新


后续规划


  1. 接入更多的框架支持
  2. 优化pdf的导出
  1. 超链接
  2. 字体图标
  1. 优化用户体验
  1. 降低jsoneditor的存在感,当前的新增与删除操作依赖jsoneditor,对不懂前端魔法的同学不友好
  2. 优化移动端的交互
  3. 美化界面
  1. 加入自动生成代码模板指令
  2. 搬运更多的简历模板


感谢你能坚持读到这里,谢谢捧场,如果你也感兴趣欢迎贡献代码(简历/功能)


相关链接


相关文章
OA项目之会议通知(查询&是否参会&反馈详情)(三)
OA项目之会议通知(查询&是否参会&反馈详情)
108 0
|
JSON jenkins 持续交付
jenkins学习笔记之四:jenkins常用pipline DSL方法
jenkins学习笔记之四:jenkins常用pipline DSL方法
|
搜索推荐 计算机视觉
赠人玫瑰,手有余香,5款实用软件推荐
今天推荐5款十分小众的软件,知道的人不多,但是每个都是非常非常好用的,有兴趣的小伙伴可以自行搜索下载。
115 0
|
存储 数据可视化 前端开发
Echarts+vue+java+mysql实现数据可视化
Echarts+vue+java+mysql实现数据可视化
416 0
|
前端开发 JavaScript 数据可视化
IT圈茶余饭后的“鄙视链”——看看前端开发有多难
IT圈茶余饭后的“鄙视链”——看看前端开发有多难
631 0
|
人工智能 自然语言处理 搜索推荐
通义大模型落地手机芯片!离线环境可流畅运行多轮AI对话
通义大模型落地手机芯片!离线环境可流畅运行多轮AI对话
353 0
|
存储 NoSQL Java
链路跟踪Jaeger使用总结
链路跟踪Jaeger使用总结
455 0
|
关系型数据库 MySQL Java
Datax及Datax-web 下载使用
Datax及Datax-web 下载使用
1370 0
|
设计模式
深入浅出组合模式
深入浅出组合模式
58 0
|
前端开发
RxSwift-双向绑定
RxSwift-双向绑定
384 0

热门文章

最新文章