zoukankan      html  css  js  c++  java
  • 富文本API

    这个笔记来自网络资料的总结
    简书大佬三省吾身_9862
    tuobaye个人博客

    富文本有相关3个API和一个新属性

    • var selection = window.getSelection();
    • var range = selection.getRangeAt;
    • document.execCommand(x,y,z);
    • 新属性 contenteditable="true"

    第一步

    <!-- 让标签变成可输入的区域 -->
    <div contenteditable="true" id="rich-editor">
    

    selection 对象和 range 对象来操作光标
    通常情况下我们不会直接操作 selection 对象,而是需要操作用 seleciton 对象所对应的用户选择的 ranges (区域),俗称拖蓝

    var selection = window.getSelection();
    var range = selection.getRangeAt(0);
    // 由于浏览器当前可能存在多个文本选取,所以 getRangeAt 函数接受一个索引值。在富文本编辑其中,我们不考虑多选取的可能性,就当上面是固定写法
    

    image.png

    这是光标位置的打印,因为光标和console不能同时操作,所以用了计时器

    image.png

    这是拖蓝位置的打印,因为光标和console不能同时操作,所以用了计时器

    上面两个图的的属性

    • startContainer: range 范围的起始节点。
    • endContainer: range 范围的结束节点
    • startOffset: range 起点位置的偏移量。
    • endOffset: range 终点位置的偏移量。
    • commonAncestorContainer: 返回包含 startContainer 和 endContainer 的最深的节点。
    • collapsed: 返回一个用于判断 Range 起始位置和终止位置是否相同的布尔值。

    selection的方法有

    selection.getRangeAt()
    selection.removeAllRanges()
    selection.addRange()
    

    range的方法有

    range.setStart(): 设置 Range 的起点
    range.setEnd(): 设置 Range 的终点
    range.selectNode(): 设定一个包含节点和节点内容的 Range
    range.collapse(): 向指定端点折叠该 Range
    range.insertNode(): 在 Range 的起点处插入节点。
    range.cloneRange(): 返回拥有和原 Range 相同端点的克隆 Range 对象
    

    所以富文本编辑器的基本操作就是,删除原有的 ranges 来保证光标一定会变动到我们想要的位置

    let selection  =  window.getSelection();
    selection.removeAllRanges();
    let range  =  document.createRange();
    range.setStart(startContainer,startOffset);
    range.setEnd(endContainer,endOffset);
    selection.addRange(range);
    

    对文本的操作

    // 一句代码实现富文本编辑器的核心
    document.execCommand(x,y,z)
    
    // x参数
    // 有选中文字,加粗,下划线,删除线,斜体
    // 有有当前光标所在行,左 居中 右 两端
    // 有有光标所在行, 右缩进 左缩进
    // 有有插入,有序列表,无序列表
    // 有有选中文字变成,上标 下标
    // 有有,全选 复制 剪切
    // 有有选中字体,字体大小
    // 有有选中字体,字体风格
    // 有有选择字体,字体颜色
    
    // y参数,就是false
    
    // z参数是需要选中的功能传进来的value
    

    具体查看上面第二个大佬的代码

    》》还需要在这上面添加几个功能,修几个bug

    插入 ul 和 ol 位置错误
    当我们调用 document.execCommand("insertUnorderedList", false, null) 来插入一个列表的时候,新的列表会被插入

    标签中,为此我们需要每次调用该命令前做一次修正,参考代码如下:

    function  adjustList(){
       let lists = document.querySelectorAll("ol, ul");
       for(let i = 0;i < lists.length;i++)  {
          let ele = lists[i];  // ol
          let parentNode = ele.parentNode;
          if(parentNode.tagName === 'P' && parentNode.lastChild === parentNode.firstChild){
             parentNode.insertAdjacentElement('beforebegin',ele);
             parentNode.remove()
          }
       } 
    }
    

    插入分割线
    调用 document.execCommand('insertHorizontalRule', false, null); 会插入一个<hr>标签
    光标和<hr>的效果一致了。为此要判断当前光标是否在<li>里面,如果是则在<hr>后面追加一个空的文本节点 #text 不是的话追加 <p><br></p>。然后将光标定位在里面,可用如下方式查找。

    function  findParentByTagName(root, name){
        let parent=root;
        if  (typeof name === "string"){
            name = [name];
        }
        while(name.indexOf(parent.nodeName.toLowerCase())===-1&&parent.nodeName!=="BODY"&&parent.nodeName!=="HTML"){
            parent=parent.parentNode;
        }
        return  parent.nodeName === "BODY" || parent.nodeName === "HTML"?null:parent;
    }
    

    插入链接
    调用 document.execCommand('createLink', false, url); 方法我们可以插入一个 url 链接,但是该方法不支持插入指定文字的链接。同时对已经有链接的位置可以反复插入新的链接。为此我们需要重写此方法。

    function  insertLink(url,title){
        let selection = document.getSelection(),
        range = selection.getRangeAt(0);
        if(range.collapsed){
            let start = range.startContainer,
            parent=Util.findParentByTagName(start,'a');
            if(parent){
                parent.setAttribute('src',url);
            }else{
                this.insertHTML(`<a href="${url}">${title}</a>`);
            }
        }else{
            document.execCommand('createLink',false,url);
        }
    }
    

    处理 paste 粘贴
    在富文本编辑器中,粘贴效果默认采用如下规则:

    1. 如果是带有格式的文本,则保留格式(格式会被转换成html标签的形式)
    2. 粘贴图文混排的内容,图片可以显示,src 为图片真实地址。
    3. 通过复制图片来进行粘贴的时候,不能粘入内容
    4. 粘贴其他格式内容,不能粘入内容

    为了能够控制粘贴的内容,我们监听 paste 事件。该事件的 event 对象中会包含一个 clipboardData 剪切板对象。

    对于规则一和规则二,我们可以利用该对象的 getData 方法来获得带有格式和不带格式的内容

    // 先判断格式
    let plainText = event.clipboardData.getData('text/plain');  // 无格式文本
    let plainHTML = event.clipboardData.getData('text/html'); // 有格式文本
    // 再选择调用
    document.execCommand('insertText', false, plainText);
    // 或
    document.execCommand('insertHTML', false, plainHTML); 
    来重写编辑上的paste效果。
    

    然而对于规则 3 ,上述方案就无法处理了。这里我们要引入 event.clipboardData.items,这是一个数组包含了所有剪切板中的内容对象,当你复制了一张图片来粘贴,那么 event.clipboardData.items 的长度就为二

    // items[0] 为图片的名称
    // items[0].kind 为 string
    // items[0].type 为 text/plain 或 text/html
    // 获取内容方式如下:
    items[0].getAsString(str => {
        // 处理 str 即可
    })
    
    // items[1] 为图片的二进制数据
    // items[1].kind 为 file
    // items[1].type 为 图片的格式
    // 想要获取里面的内容,我们就需要创建 FileReader 对象了,示例代码如下:
    
    let file = items[1].getAsFile();
    // file.size 为文件大小
    let reader = new FileReader();
    reader.onload = function()  {
        // reader.result 为文件内容,就可以做上传操作了
    }
    
    if(/image/.test(item.type))  {
        reader.readAsDataURL(file); // 读取为 base64 格式
    }
    

    插入图片失败

    document.execCommand('insertImage', 'false', url);
    

    如果编辑器失去了焦点,那么 selection 和 range 对象将销毁。因此调用 insertImage 时,并不能获得光标所在位置,因此失败。为此需要增加,backupRange() 和 restoreRange() 函数。当页面失去焦点的时候记录 range 信息,插入图片前恢复 range 信息

    // 需要在光标离开时记录位置,使用的是最上面的两句代码
    var selection = window.getSelection();
    var range = selection.getRangeAt(0);
    
    // 当用户选择了图片文件后,执行fous()
    document.querySelector("#rich-editor").focus()
    
    // 然后移动光标到之前的位置
    // 再执行
    document.execCommand('insertImage', 'false', url);
    
  • 相关阅读:
    如何理解c和c++ 的复杂类型声明
    xp自带扫雷bug
    求三角形的外接圆
    判断一个点是否在一个三角形内
    三角函数角度公式
    弗洛伊德(Floyd)算法
    在Win32应用程序中用Console控制台输出信息
    无法打开libcp.lib
    C#获取当前应用程序所在路径及环境变量
    C#事件的发送方和接收方(订阅方)【转】
  • 原文地址:https://www.cnblogs.com/pengdt/p/12037988.html
Copyright © 2011-2022 走看看