JavaScript实战笔记(四) 选中文本高亮

简介: JavaScript实战笔记(四) 选中文本高亮

以下代码可以实现用户选中文本之后点击高亮按钮,高亮选中文本,支持多种颜色高亮

由于博主最近事情比较多,所以只是稍微写了个例子,有不足之处,请多多见谅


<!DOCTYPE html>
<html>
<head>
    <title>选中文本高亮</title>
    <style>
        button {
            margin: 10px;
            padding: 5px;
        }
        span {
            word-wrap: break-word;
            word-break: break-all;
        }
    </style>
    <script>
        function createRandomNumber(start, end) {
            return (end >= start) ? Math.floor(Math.random() * (end - start) + start) : null
        }
        function createRandomString(length, source) {
            var length = length || 52
            var source = source || 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
            var result = []
            for (let i = 1; i <= length; i += 1) result.push(source.charAt(createRandomNumber(0, source.length)))
            return result.join('')
        }
        function initDom() {
            let bId = ['redHighlight', 'greenHighlight', 'blueHighlight']
            let bFragment = document.createDocumentFragment()
            for (let i = 0, ii = bId.length; i < ii; i += 1) {
                let button = document.createElement('button')
                let string = bId[i]
                button.id = string
                button.innerHTML = string.substring(string.length - 9)
                button.style.color = string.substring(0, string.length - 9)
                bFragment.appendChild(button)
            }
            document.body.appendChild(bFragment)
            let div = document.createElement('div')
            div.id = 'article'
            document.body.appendChild(div)
            let sCount = 10
            let sFragment = document.createDocumentFragment()
            for (let i = 0, ii = sCount; i < ii; i += 1) {
                let span = document.createElement('span')
                span.id = 'highlightable-' + i.toString()
                span.innerHTML = createRandomString()
                sFragment.appendChild(span)
            }
            document.getElementById('article').appendChild(sFragment)
            return bId
        }
        function initData() {
            let result = {}
            let collection = document.getElementById('article').getElementsByTagName('span')
            Array.prototype.forEach.call(collection, (item) => {
                result[item.id] = {}
                result[item.id]['htmlString'] = item.innerHTML
                result[item.id]['infoArray'] = []
            })
            return result
        }
        function updateInfo(data, id, newInfo) {
            let infoArray = data[id]['infoArray']
            let result = []
            let [newStartIdx, newEndIdx, newColor] = newInfo
            for (let orgInfo of infoArray) {
                let [orgStartIdx, orgEndIdx, orgColor] = orgInfo
                if (orgStartIdx >= newStartIdx && orgStartIdx <= newEndIdx) {
                    if (orgEndIdx >= newStartIdx && orgEndIdx <= newEndIdx) continue
                    else result.push([newEndIdx, orgEndIdx, orgColor])
                }
                else if (orgEndIdx >= newStartIdx && orgEndIdx <= newEndIdx) {
                    if (orgStartIdx >= newStartIdx && orgStartIdx <= newEndIdx) continue
                    else result.push([orgStartIdx, newStartIdx, orgColor])
                }
                else if (orgStartIdx <= newStartIdx && orgEndIdx >= newEndIdx) {
                    result.push([orgStartIdx, newStartIdx, orgColor])
                    result.push([newEndIdx, orgEndIdx, orgColor])
                }
                else {
                    result.push([orgStartIdx, orgEndIdx, orgColor])
                }
            }
            result.push(newInfo)
            result.sort((a, b) => { return a[0] <= b[0] ? a[0] == b[0] ? 0 : 1 : -1 })
            data[id]['infoArray'] = result
        }
        function highlightHelper(data, id) {
            let htmlString = data[id]['htmlString']
            let infoArray = data[id]['infoArray']
            let result = htmlString
            for (let info of infoArray) {
                let [startIdx, endIdx, color] = info
                let startHtml = result.substring(0, startIdx)
                let middleHtml = result.substring(startIdx, endIdx)
                let endHtml = result.substring(endIdx)
                result = startHtml + `<span style="color:${color}">` + middleHtml + '</span>' + endHtml
            }
            return result
        }
        function highlightSelection(data, color) {
            let selection = document.getSelection()
            if (selection.type !== 'Range') return
            let range = selection.getRangeAt(0)
            if (range.length === 0) return
            let startContainer = range.startContainer
            let endContainer = range.endContainer
            let startOffset = range.startOffset
            let endOffset = range.endOffset
            let startNode = startContainer.parentElement
            while (startNode.id.search(/highlightable-[0-9]+/) === -1) startNode = startNode.parentElement
            let endNode = endContainer.parentElement
            while (endNode.id.search(/highlightable-[0-9]+/) === -1) endNode = endNode.parentElement
            let currNode = startNode
            while (true) {
                let startIdx = 0
                let endIdx = 0
                let isStartNode = (currNode === startNode)
                let isEndNode = (currNode === endNode)
                if (isStartNode && isEndNode) {
                    let isFinishStart = false
                    let isFinishEnd = false
                    for (let child of currNode.childNodes) {
                        let isTextNode = (child.tagName == null)
                        let isStartContainer = (child === startContainer || child === startContainer.parentElement)
                        let isEndContainer = (child === endContainer || child === endContainer.parentElement)
                        let childLength = isTextNode ? child.length : child.innerHTML.length
                        if (!isFinishStart && !isStartContainer) {
                            startIdx += childLength
                            endIdx += childLength
                        } else if (!isFinishStart && isStartContainer && !isEndContainer) {
                            startIdx += startOffset
                            endIdx += childLength
                            isFinishStart = true
                        } else if (!isFinishStart && isStartContainer && isEndContainer) {
                            startIdx += startOffset
                            endIdx += endOffset
                            isFinishStart = true
                            isFinishEnd = true
                        } else if (isFinishStart && !isFinishEnd && !isEndContainer) {
                            endIdx += childLength
                        } else if (isFinishStart && !isFinishEnd && isEndContainer) {
                            endIdx += endOffset
                            isFinishEnd = true
                        } else if (isFinishEnd) {
                        }
                    }
                } else {
                    if (isStartNode) {
                        let isFinishStart = false
                        for (let child of currNode.childNodes) {
                            let isTextNode = (child.tagName == null)
                            let childLength = isTextNode ? child.length : child.innerHTML.length
                            let isStartContainer = (child === startContainer || child === startContainer.parentElement)
                            endIdx += childLength
                            if (!isFinishStart && !isStartContainer) {
                                startIdx += childLength
                            } else if (!isFinishStart && isStartContainer) {
                                startIdx += startOffset
                                isFinishStart = true
                            } else if (isFinishStart) {
                            }
                        }
                    } else if (isEndNode) {
                        let isFinishEnd = false
                        for (let child of currNode.childNodes) {
                            let isTextNode = (child.tagName == null)
                            let childLength = isTextNode ? child.length : child.innerHTML.length
                            let isEndContainer = (child === endContainer || child === endContainer.parentElement)
                            if (!isFinishEnd && !isEndContainer) {
                                endIdx += childLength
                            } else if (!isFinishEnd && isEndContainer) {
                                endIdx += endOffset
                                isFinishEnd = true
                            } else if (isFinishEnd) {
                            }
                        }
                    } else {
                        for (let child of currNode.childNodes) {
                            let isTextNode = (child.tagName == null)
                            let childLength = isTextNode ? child.length : child.innerHTML.length
                            endIdx += childLength
                        }
                    }
                }
                updateInfo(data, currNode.id, [startIdx, endIdx, color])
                currNode.innerHTML = highlightHelper(data, currNode.id)
                if (currNode === endNode) break
                currNode = currNode.nextElementSibling
            }
            document.getSelection().empty()
        }
        window.onload = function() {
            let buttonId = initDom()
            let globalData = initData()
            for (let button of buttonId) {
                document.getElementById(button).addEventListener('click', () => {
                    highlightSelection(globalData, button.substring(0, button.length - 9))
                })
            }
        }
    </script>
</head>
<body>
</body>
</html>


实现的效果如下:

20210501210710157.gif

目录
相关文章
|
30天前
|
存储 安全 API
Next.js 实战 (九):使用 next-auth 完成第三方身份登录验证
这篇文章介绍了next-auth,一个为Next.js设计的身份验证库,支持多种认证方式,如电子邮件和密码、OAuth2.0提供商(如Google、GitHub、Facebook等)以及自定义提供商。文章包含了如何配置Github Provider以及会话管理,并提到了适配器Adapters在next-auth中的作用。最后,文章强调了next-auth的强大功能值得进一步探索。
72 10
|
1月前
|
设计模式 数据安全/隐私保护
Next.js 实战 (七):浅谈 Layout 布局的嵌套设计模式
这篇文章介绍了在Next.js框架下,如何处理中后台管理系统中特殊页面(如登录页)不包裹根布局(RootLayout)的问题。作者指出Next.js的设计理念是通过布局的嵌套来创建复杂的页面结构,这虽然保持了代码的整洁和可维护性,但对于特殊页面来说,却造成了不必要的布局包裹。文章提出了一个解决方案,即通过判断页面的skipGlobalLayout属性来决定是否包含RootLayout,从而实现特殊页面不包裹根布局的目标。
90 33
|
1月前
|
中间件 API
Next.js 实战 (八):使用 Lodash 打包构建产生的“坑”?
这篇文章介绍了作者在使用Nextjs15进行项目开发时遇到的部署问题。在部署过程中,作者遇到了打包构建时的一系列报错,报错内容涉及动态代码评估在Edge运行时不被允许等问题。经过一天的尝试和调整,作者最终删除了lodash-es库,并将radash的部分源码复制到本地,解决了打包报错的问题。文章最后提供了项目的线上预览地址,并欢迎读者留言讨论更好的解决方案。
41 10
|
2月前
|
前端开发 API 开发者
Next.js 实战 (五):添加路由 Transition 过渡效果和 Loading 动画
这篇文章介绍了Framer Motion,一个为React设计的动画库,提供了声明式API处理动画和页面转换,适合创建响应式用户界面。文章包括首屏加载动画、路由加载Loading、路由进场和退场动画等主题,并提供了使用Framer Motion和next.js实现这些动画的示例代码。最后,文章总结了这些效果,并邀请读者探讨更好的实现方案。
|
1月前
|
JavaScript 前端开发 API
Next.js 实战 (六):如何实现文件本地上传
这篇文章介绍了在Next.js中如何实现文件上传到本地的方法。文章首先提到Next.js官方文档中没有提供文件上传的实例代码,因此开发者需要自行实现,通常有两种思路:使用Node.js原生上传或使用第三方插件如multer。接着,文章选择了使用Node.js原生上传的方式来讲解实现过程,包括如何通过哈希值命名文件、上传到指定目录以及如何分类文件夹。然后,文章展示了具体的实现步骤,包括编写代码来处理文件上传,并给出了代码示例。最后,文章通过一个效果演示说明了如何通过postman模拟上传文件,并展示了上传后的文件夹结构。
|
26天前
|
监控 安全 中间件
Next.js 实战 (十):中间件的魅力,打造更快更安全的应用
这篇文章介绍了什么是Next.js中的中间件以及其应用场景。中间件可以用于处理每个传入请求,比如实现日志记录、身份验证、重定向、CORS配置等功能。文章还提供了一个身份验证中间件的示例代码,以及如何使用限流中间件来限制同一IP地址的请求次数。中间件相当于一个构建模块,能够简化HTTP请求的预处理和后处理,提高代码的可维护性,有助于创建快速、安全和用户友好的Web体验。
|
2月前
Next.js 实战 (二):搭建 Layouts 基础排版布局
本文介绍了作者在Next.js v15.x版本发布后,对一个旧项目的重构过程。文章详细说明了项目开发规范配置、UI组件库选择(最终选择了Ant-Design)、以及使用Ant Design的Layout组件实现中后台布局的方法。文末展示了布局的初步效果,并提供了GitHub仓库链接供读者参考学习。
Next.js 实战 (二):搭建 Layouts 基础排版布局
|
2月前
|
存储 网络架构
Next.js 实战 (四):i18n 国际化的最优方案实践
这篇文章介绍了Next.js国际化方案,作者对比了网上常见的方案并提出了自己的需求:不破坏应用程序的目录结构和路由。文章推荐使用next-intl库来实现国际化,并提供了详细的安装步骤和代码示例。作者实现了国际化切换时不改变路由,并把当前语言的key存储到浏览器cookie中,使得刷新浏览器后语言不会失效。最后,文章总结了这种国际化方案的优势,并提供Github仓库链接供读者参考。
103 5
|
2月前
Next.js 实战 (三):优雅的实现暗黑主题模式
这篇文章介绍了在Next.js中实现暗黑模式的具体步骤。首先,需要安装next-themes库。然后,在/components/ThemeProvider/index.tsx文件中新增ThemeProvider组件,并在/app/layout.tsx文件中注入该组件。如果想要加入过渡动画,可以修改代码实现主题切换时的动画效果。最后,需要在需要的位置引入ThemeModeButton组件,实现暗黑模式的切换。
|
3月前
|
设计模式 前端开发 JavaScript
JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式
本文深入探讨了JavaScript设计模式及其在实战中的应用,涵盖单例、工厂、观察者、装饰器和策略模式,结合电商网站案例,展示了设计模式如何提升代码的可维护性、扩展性和可读性,强调了其在前端开发中的重要性。
57 2

热门文章

最新文章