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

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

高能操作


高亮变动的内容


诉求:在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. 搬运更多的简历模板


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


相关链接


相关文章
|
6月前
|
存储 前端开发 搜索推荐
​开源在线生成简历器:快速打造个人的简历利器
​开源在线生成简历器:快速打造个人的简历利器
153 0
|
6月前
|
人工智能 算法 JavaScript
【简历优化平台-06】为什么很多简历必须写项目经验?有的简历没有项目经验?
【简历优化平台-06】为什么很多简历必须写项目经验?有的简历没有项目经验?
|
前端开发 算法 JavaScript
【简历优化平台开发教程-12】测试用模版简历!
【简历优化平台开发教程-12】测试用模版简历!
|
运维 前端开发 架构师
适合小白的人事面试回答模板
当我们有幸通过几轮技术面试后,最有心机的人事面试就来了,人事面试不是技术面试,会就是会,不会就是不会,这是一个勾心斗角的过程,好些不善于表达的工程师也有可能在人事面试被刷掉,以下我列举了一下常见的人事面试问题
147 0
|
数据可视化 数据管理 BI
招聘管理综合实践——生成在线简历库|学习笔记
快速学习招聘管理综合实践——生成在线简历库
招聘管理综合实践——生成在线简历库|学习笔记
|
弹性计算 Linux 云计算
将简历上传到服务器 | 学习笔记
快速学习 将简历上传到服务器
将简历上传到服务器 | 学习笔记
|
消息中间件 前端开发 JavaScript
校招大学生简历制作模板(ps:程序员简历)
本文献给准备春招秋招的小伙伴们!
229 0
校招大学生简历制作模板(ps:程序员简历)
|
弹性计算
搭建自己的简历平台
我是就读于广东财经大学物流管理专业的一名大二学生,在老师的课堂指导下,参与了飞天加速计划,领用到云服务器ECS,并运用云服务器ECS搭建了自己的简历平台和个人博客,接下来,我将简单介绍一下我利用云服务器ECS搭建简历平台的过程。
搭建自己的简历平台
|
数据库 数据安全/隐私保护 Python
【Django | 开发】面试招聘信息网站(用户登录注册&投在线递简历)
【Django | 开发】面试招聘信息网站(用户登录注册&投在线递简历)
【Django | 开发】面试招聘信息网站(用户登录注册&投在线递简历)
|
JSON 前端开发 JavaScript
我打造了一个简历在线生成应用(一)
我打造了一个简历在线生成应用(一)