vue2 PC线上阅读图书-修改背景色功能(包括多段落选中文本修改背景色)window.getSelection()、document.execComm()、range.cloneContents()

前言: 目前 document.execCommand('BackColor', false, color) 方法被弃用了各位谨慎使用!!!

功能思路:window.getSelection() 获取选中文本范围,document.execCommand('BackColor', false, color) 修改选中范围文本背景色,range.cloneContents()  获取选中的文本节点与父节点,将document.execCommand 方法生成的所有 span 标签获取到,然后使用 dom 操作将 span 标签替换为 i 标签并为 i 标签设置自定义属性(方便后续使用自定义属性查询相同的文本联动修改文本样式) 自定义属性值可使用 require('uuid') 库。

document.execCommand('BackColor', false, color)  接口地址:

document.execCommand - Web API 接口参考 | MDN

这里可以选择中文哦~  vue2 PC线上阅读图书-修改背景色功能(包括多段落选中文本修改背景色)window.getSelection()、document.execComm()、range.cloneContents()_第1张图片

文本:

本书在讲解HTML时,已经对如何在网页中使用文字做了详细的介绍。本章将以CSS的样式定义方法来介绍文字的使用。CSS的文字样式定义更加丰富,实用性更强。通过本章的学习,读者能随心所欲地在网页制作中完成文字的制作。同时,本章还会介绍如何利用CSS的样式定义进行版面编排,如何丰富段落的制作样式等。本章的思维导图如下。

本书在讲解HTML时,已经对如何在网页中使用文字做了详细的介绍。本章将以CSS的样式定义方法来介绍文字的使用。CSS的文字样式定义更加丰富,实用性更强。通过本章的学习,读者能随心所欲地在网页制作中完成文字的制作。同时,本章还会介绍如何利用CSS的样式定义进行版面编排,如何丰富段落的制作样式等。本章的思维导图如下。

本书在讲解HTML时,已经对如何在网页中使用文字做了详细的介绍。本章将以CSS的样式定义方法来介绍文字的使用。CSS的文字样式定义更加丰富,实用性更强。通过本章的学习,读者能随心所欲地在网页制作中完成文字的制作。同时,本章还会介绍如何利用CSS的样式定义进行版面编排,如何丰富段落的制作样式等。本章的思维导图如下。

本书在讲解HTML时,已经对如何在网页中使用文字做了详细的介绍。本章将以CSS的样式定义方法来介绍文字的使用。CSS的文字样式定义更加丰富,实用性更强。通过本章的学习,读者能随心所欲地在网页制作中完成文字的制作。同时,本章还会介绍如何利用CSS的样式定义进行版面编排,如何丰富段落的制作样式等。本章的思维导图如下。

font-family属性可以同时声明多种字体,字体之间用逗号分隔。另外,一些字体的名称中间会出现空格,例如上面的Times New Roman,这时需要用双引号将其引起来,使浏览器知道这是一种字体的名称。注意,不要使用中文输入状态下的双引号,而要使用英文输入状态下的双引号。

修改 i 标签倾斜字体:

i {
    font-style: normal;
}

方法:

获取选中的文本内容

  // vue2  
  // 获取选中文本内容
        mouseup_highlight(event) {
            var that = this
            that.child_bgcolor_flag = 'hidden'
            // 判断是否有选中的文本
            if (Object.keys(that.sel).length === 0) {
                that.active_event = event.target // 选中标签
                that.sel = window.getSelection() // 选中文本
                // isCollapsed:只读属性,返回一个布尔值用于描述选区的起点和终点是否在同一个位置(表明是否进行了框选)。
                if (!that.sel.isCollapsed) {
                    if (that.sel.toString().length > 300) {
                        this.$message({
                            message: '选中的文字不得超过300个!',
                            type: 'warning'
                        });
                        // 取消选中文本
                        document.getSelection().removeAllRanges()
                        return false
                    }
                    that.toolbar_flag = true
                } else {
                    // 判断当前点击的是 span 标签
                    if (event.target.tagName === 'SPAN') {
                        that.toolbar_flag = true
                        that.child_bgcolor_flag = 'show'
                    } else if (event.target.tagName === 'I') {
                        // 点击标签 获取当前标签的自定义属性 data-highlight-id
                        that.active_uuid = event.target.getAttribute('data-highlight-id')
                        that.toolbar_flag = true
                        that.child_bgcolor_flag = 'show'
                    } else {
                        // 关闭工具栏方法
                        that.close_toolbar()
                    }
                }
            }
            // console.log(event,)
            // 调整工具栏定位
            if (this.toolbar_flag) {
                this.range = this.sel.getRangeAt(0);
                // console.log(this.range)
                let position = this.range.getBoundingClientRect();
                // window.scrollX 和 window.scrollY 分别表示页面在 x 轴和 y 轴上的滚动位置。
                // position.left - 250 :250为工具栏的一半宽度。
                let x = (position.left - 250) + (position.width / 2) + window.scrollX; // 左边距加上滚动位置的x坐标  
                let y = (position.top - 65) + window.scrollY; // 上边距加上滚动位置的y坐标  
                this.position_left = x + 'px'
                this.position_top = y + 'px'
            }
        },

工具栏重点:

/* 需要使用,否则 window.getSelection() 会选中工具栏 */
.noselect {
    user-select: none !important;
    /* 禁止文本选择 */
    -webkit-user-select: none !important;
    /* 禁止文本选择 (WebKit) */
    -moz-user-select: none !important;
    /* 禁止文本选择 (Firefox) */
}

点击工具栏按钮触发修改文本功能:

修改了一下,现在不管是从后向前选中文本、已经修改过的 i 标签中从中间向两边选择,跨段落选择等等都可以啦 (将渲染完成后的数据保存这个地方有点问题,记得修改哦)

比较繁琐,欢迎各位大佬优化评论。

vue2 PC线上阅读图书-修改背景色功能(包括多段落选中文本修改背景色)window.getSelection()、document.execComm()、range.cloneContents()_第2张图片

 // 修改选中文本样式
        changeDocument(color) {
            this.follow_styleNode = null
            // this.active_uuid 如果有值说明是需要修改的文本,没有值 就是需要创建背景色的文本
            if (this.active_uuid) {
                this.activeElementI({ color, type: 'line', lineType: 'bg-line-highlight' })
                return false
            }
            const { v4: uuidv4 } = require('uuid');  //uuid 库 直接引用即可
            this.active_uuid = uuidv4(); // 生成一个UUID  

            var sel = this.sel
            let range = null
            if (sel.rangeCount && sel.getRangeAt) {
                range = sel.getRangeAt(0)
            }
            // 重新设置选中范围!!!重要 
            // 不重新设置的话 会造成用户从后向前选中文本出问题报错。 sel.toString() 获取不到文本
            range.setStart(range.startContainer, range.startOffset);
            range.setEnd(range.endContainer, range.endOffset);
            // 判断是否选中了已经修改完成的 i 标签
            if (sel.anchorNode.parentNode.nodeName == 'I') {
                this.follow_styleNode = sel.anchorNode.parentNode
                // 将范围起点设置在dom之后
                range.setStartAfter(sel.anchorNode.parentNode);
            }
            if (sel.extentNode.parentNode.nodeName == 'I') {
                if (!this.follow_styleNode) {
                    this.follow_styleNode = sel.extentNode.parentNode
                }
                // 将范围终点设置在dom之前
                range.setEndBefore(sel.extentNode.parentNode);
            }
            console.log(sel.toString())
            // 开启文档编辑
            document.designMode = 'on'
            if (range) {
                sel.removeAllRanges();
                sel.addRange(range)
            }
            document.execCommand('BackColor', false, color)   // 已弃用方法
            this.SpanElementChangeI(color, this.active_uuid, range)
            // 添加数据到JSON
            this.pushTextJSON(sel, color)
            // 关闭文档编辑
            document.designMode = 'off'
            // 取消文本选中
            window.getSelection().removeAllRanges();
        },
        // 将span 标签改为 i 标签 
        SpanElementChangeI(color, uuid, range, className = '', textDecorationColor = '') {
            var selectedNodes = range.cloneContents(); // 获取选中的文本节点
            // 如果 follow_styleNode 节点不为null 说明选中起点或选中终点包裹了其他i标签,使用其他i标签的样式即可
            if (this.follow_styleNode) {
                console.log(this.follow_styleNode, 'this.follow_styleNode')
                color = this.follow_styleNode.style.backgroundColor
                uuid = this.follow_styleNode.getAttribute('data-highlight-id')
                className = this.follow_styleNode.className
                textDecorationColor = this.follow_styleNode.style.textDecorationColor
                this.active_uuid = uuid
            }
            if (color == 'transparent') {
                this.changeIndex(1)
            } else {
                this.changeIndex(0)
            }
            if (selectedNodes.childNodes.length > 0) {
                // 多个选中文本
                console.log(selectedNodes.childNodes, 'list')
                var iElement = null
                // 遍历节点获取选中文本中有其他标签,如果有获取第一个标签的样式,
                for (let index = 0; index < selectedNodes.childNodes.length; index++) {
                    const element = selectedNodes.childNodes[index];
                    let item
                    if (element.tagName === 'SPAN') {
                        // 判断是否选中了已经修改完成的 i 标签
                        if (this.sel.anchorNode.parentNode.nodeName == 'SPAN') {
                            item = this.sel.anchorNode.parentNode.parentNode
                        } else if (this.sel.extentNode.parentNode.nodeName == 'SPAN') {
                            item = this.sel.extentNode.parentNode.parentNode
                        }
                    } else if (element.tagName === 'P') {
                        item = document.getElementById(element.id)
                    } else {
                        if (!element.previousSibling) {
                            if (this.sel.anchorNode.parentNode.parentNode.nodeName == 'P') {
                                item = this.sel.anchorNode.parentNode.parentNode
                            } else {
                                item = this.sel.anchorNode.parentNode.parentNode.parentNode
                            }
                        } else {
                            continue
                        }
                    }
                    console.log(item, 'item')
                    // 获取 

标签下的所有 节点 let spanNode = item.getElementsByTagName('span'); console.log(spanNode[0], 'spanNode') if (!iElement && !this.follow_styleNode) { try { iElement = spanNode[0].querySelector('i'); console.log(iElement, 'iElement') color = iElement.style.backgroundColor || 'transparent' // uuid = iElement.getAttribute('data-highlight-id') // this.active_uuid = uuid className = iElement.className || '' textDecorationColor = iElement.style.textDecorationColor || '' console.log(color, className) if (color == 'transparent') { this.changeIndex(1) } else { this.changeIndex(0) } } catch (err) { console.log(err, 'err') } } } // 遍历修改节点 for (let index = 0; index < selectedNodes.childNodes.length; index++) { const element = selectedNodes.childNodes[index]; let item if (element.tagName === 'SPAN') { // 判断是否选中了已经修改完成的 i 标签 if (this.sel.anchorNode.parentNode.nodeName == 'SPAN') { item = this.sel.anchorNode.parentNode.parentNode } else if (this.sel.extentNode.parentNode.nodeName == 'SPAN') { item = this.sel.extentNode.parentNode.parentNode } } else if (element.tagName === 'P') { item = document.getElementById(element.id) } else { if (!element.previousSibling) { if (this.sel.anchorNode.parentNode.parentNode.nodeName == 'P') { item = this.sel.anchorNode.parentNode.parentNode } else { item = this.sel.anchorNode.parentNode.parentNode.parentNode } } else { continue } } console.log(item, 'item') // 获取

标签下的所有 节点 let spanNode = item.getElementsByTagName('span'); console.log(spanNode[0], 'spanNode') // 将 节点修改为 标签 let newNode = document.createElement('i'); newNode.textContent = spanNode[0].textContent; newNode.style.backgroundColor = color; newNode.className = className; newNode.style.textDecorationColor = textDecorationColor // 为标签添加自定义属性 data-highlight-id // data-highlight-id 高亮id 后续通过选中节点获取高亮 id 再获取所有相同高亮 id 的节点操作dom修改样式 newNode.setAttribute('data-highlight-id', uuid) console.log(className, color) item.replaceChild(newNode, spanNode[0]); } } else { // 单个选中文本 let element if (range.startContainer.nodeName === 'P') { element = range.startContainer } else { element = range.startContainer.parentNode; } if (!element.id) { return false } console.log(element, 'element') const item = document.getElementById(element.id) // 获取

标签下的所有 节点 let spanNode = item.getElementsByTagName('span'); console.log(spanNode[0], 'spanNode') if (!spanNode[0]) { return false } // 将 节点修改为 标签 let newNode = document.createElement('i'); newNode.textContent = spanNode[0].textContent; newNode.style.backgroundColor = color; newNode.style.textDecorationColor = textDecorationColor; newNode.className = className; // 为标签添加自定义属性 data-highlight-id newNode.setAttribute('data-highlight-id', uuid) item.replaceChild(newNode, spanNode[0]); } },

 完整代码:数据:test.json、 页面Home.vue 、工具栏:toolbar.vue

test.json    使用require引用   require('@/statics/json/test.json') 

注意:数据不准确,随便拿来用的而已。

[
    {
        "type": "line",
        "id": 2696,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-03T15:15:05.4967859",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": "bg-line-highlight",
        "color": "blue-color-highlight",
        "isPublic": false,
        "articleId": 42,
        "text": "已经对如何在",
        "location": "{\"startMeta\":{\"blockId\":\"n_8e9E7Mcj0UXKJCcdH9N\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":12},\"endMeta\":{\"blockId\":\"n_8e9E7Mcj0UXKJCcdH9N\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":17},\"text\":\"解HTML时,\",\"id\":\"ba0d4cef-a4cd-495d-875e-1b82a874af4d\",\"__isHighlightSource\":{},\"extra\":\"line\"}",
        "startBlockId": "n_8e9E7Mcj0UXKJCcdH9N",
        "endBlockId": "n_8e9E7Mcj0UXKJCcdH9N",
        "body": "",
        "images": [],
        "bgColor": "#80b3f8"
    },
    {
        "type": "line",
        "id": 2696,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-03T15:15:05.4967859",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": "bg-line-highlight",
        "color": "pink-color-highlight",
        "isPublic": false,
        "articleId": 42,
        "text": "解HTML时,",
        "location": "{\"startMeta\":{\"blockId\":\"n_8e9E7Mcj0UXKJCcdH9N\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":4},\"endMeta\":{\"blockId\":\"n_8e9E7Mcj0UXKJCcdH9N\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":11},\"text\":\"解HTML时,\",\"id\":\"ba0d4cef-a4cd-495d-875e-1b82a874af4d\",\"__isHighlightSource\":{},\"extra\":\"line\"}",
        "startBlockId": "n_8e9E7Mcj0UXKJCcdH9N",
        "endBlockId": "n_8e9E7Mcj0UXKJCcdH9N",
        "body": "",
        "images": [],
        "bgColor": "#f0949d"
    },
    {
        "type": "line",
        "id": 2697,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-03T15:15:26.3300584",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": "bg-line-highlight",
        "color": "yellow-color-highlight",
        "isPublic": false,
        "articleId": 42,
        "text": "字做了详细的",
        "location": "{\"startMeta\":{\"blockId\":\"C1Oo4s8t890XETTQWiW28\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":23},\"endMeta\":{\"blockId\":\"C1Oo4s8t890XETTQWiW28\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":29},\"text\":\"字做了详细的\",\"id\":\"f98abf2b-e0db-4313-84b2-9dc4698612dc\",\"__isHighlightSource\":{},\"extra\":\"line\"}",
        "startBlockId": "C1Oo4s8t890XETTQWiW28",
        "endBlockId": "C1Oo4s8t890XETTQWiW28",
        "body": "",
        "images": [],
        "bgColor": "#f1ce91"
    },
    {
        "type": "note",
        "id": 2698,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-03T15:15:33.4842745",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": "",
        "color": "blue-color-highlight",
        "isPublic": false,
        "articleId": 42,
        "text": "的样式定义方法",
        "location": "{\"startMeta\":{\"blockId\":\"QAG5S98SMs_Hj9WJcbt7B\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":39},\"endMeta\":{\"blockId\":\"QAG5S98SMs_Hj9WJcbt7B\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":46},\"text\":\"的样式定义方法\",\"id\":\"0c50bf72-5f3f-4b11-8dcf-ab8d32ca1bda\",\"__isHighlightSource\":{},\"extra\":\"none\"}",
        "startBlockId": "QAG5S98SMs_Hj9WJcbt7B",
        "endBlockId": "QAG5S98SMs_Hj9WJcbt7B",
        "body": "强强强强强强强强强强强强",
        "images": [],
        "bgColor": "#80b3f8"
    },
    {
        "type": "note",
        "id": 2747,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-05T16:40:58.0280648",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": "",
        "color": "",
        "isPublic": false,
        "articleId": 42,
        "text": "页制作",
        "location": "{\"startMeta\":{\"blockId\":\"fh_lL1aFbppZgNXoligpy\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":10},\"endMeta\":{\"blockId\":\"fh_lL1aFbppZgNXoligpy\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":20},\"text\":\"页制作\",\"id\":\"1f5832e2-b60e-49d8-994c-e22a97454e66\",\"__isHighlightSource\":{},\"extra\":\"none\"}",
        "startBlockId": "fh_lL1aFbppZgNXoligpy",
        "endBlockId": "fh_lL1aFbppZgNXoligpy",
        "body": "嗡嗡嗡嗡嗡嗡嗡嗡嗡",
        "images": [],
        "bgColor": "#f0949d"
    },
    {
        "type": "line",
        "id": 2747,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-05T16:40:58.0280648",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": "",
        "color": "",
        "isPublic": false,
        "articleId": 42,
        "text": "页制作",
        "location": "{\"startMeta\":{\"blockId\":\"fh_lL1aFbppZgNXoligpE\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":30},\"endMeta\":{\"blockId\":\"fh_lL1aFbppZgNXoligpE\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":38},\"text\":\"页制作\",\"id\":\"1f5832e2-b60e-49d8-994c-e22a97454e66\",\"__isHighlightSource\":{},\"extra\":\"none\"}",
        "startBlockId": "fh_lL1aFbppZgNXoligpE",
        "endBlockId": "fh_lL1aFbppZgNXoligpE",
        "body": "111111111111111111",
        "images": [],
        "bgColor": "#80b3f8"
    },
    {
        "type": "note",
        "id": 2747,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-05T16:40:58.0280648",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": "",
        "color": "",
        "isPublic": false,
        "articleId": 42,
        "text": "页制作",
        "location": "{\"startMeta\":{\"blockId\":\"Tw1ewnmTN7wCkVeDsbTnB\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":30},\"endMeta\":{\"blockId\":\"Tw1ewnmTN7wCkVeDsbTnB\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":38},\"text\":\"页制作\",\"id\":\"1f5832e2-b60e-49d8-994c-e22a97454e66\",\"__isHighlightSource\":{},\"extra\":\"none\"}",
        "startBlockId": "Tw1ewnmTN7wCkVeDsbTnB",
        "endBlockId": "Tw1ewnmTN7wCkVeDsbTnB",
        "body": "111111111111111111",
        "images": [],
        "bgColor": "#80b3f8"
    },
    {
        "type": "discuss",
        "id": 213,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-11T11:32:29.3955837",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": null,
        "color": null,
        "isPublic": false,
        "articleId": 42,
        "text": "ily属性可",
        "location": "{\"startMeta\":{\"blockId\":\"zq3UQtmr0D2EGwoPd_Ffr\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":9},\"endMeta\":{\"blockId\":\"zq3UQtmr0D2EGwoPd_Ffr\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":15},\"text\":\"素为单位时必须要加\",\"id\":\"84bf3af3-112a-404d-998f-eb7ddc8da86b\",\"__isHighlightSource\":{},\"extra\":\"none\"}",
        "startBlockId": "zq3UQtmr0D2EGwoPd_Ffr",
        "endBlockId": "zq3UQtmr0D2EGwoPd_Ffr",
        "body": "1111111111",
        "images": [],
        "bgColor": "#80b3f8"
    },
    {
        "type": "discuss",
        "id": 213123,
        "userId": 1523,
        "userNickName": "166***1217",
        "createTime": "2024-01-11T11:32:29.3955837",
        "articleName": "第5章 用CSS设置文字样式",
        "articleRank": 13,
        "lineType": null,
        "color": null,
        "isPublic": false,
        "articleId": 42,
        "text": "素为单位时必须要加",
        "location": "{\"startMeta\":{\"blockId\":\"zq3UQtmr0D2EGwoPd_Ffr\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":1},\"endMeta\":{\"blockId\":\"zq3UQtmr0D2EGwoPd_Ffr\",\"parentTagName\":\"P\",\"parentIndex\":-2,\"textOffset\":6},\"text\":\"素为单位时必须要加\",\"id\":\"84bf3af3-112a-404d-998f-eb7ddc8da86b\",\"__isHighlightSource\":{},\"extra\":\"none\"}",
        "startBlockId": "zq3UQtmr0D2EGwoPd_Ffr",
        "endBlockId": "zq3UQtmr0D2EGwoPd_Ffr",
        "body": "你好",
        "images": [],
        "bgColor": "#80b3f8"
    }
]

Home.vue:

// vue2

  


  

toolbar.vue :





你可能感兴趣的:(vue,前端,javascript,html,vue.js)