range对象实现DOM精确操作


MDN接口文档: https://developer.mozilla.org/zh-CN/docs/Web/API/Range

Range定义

我们先来看下MDN对range对象的定义:Range 接口表示一个包含节点与文本节点的一部分的文档片段。

使用方法

我们可以用 Document 对象的 Document.createRange 方法创建 Range,也可以用 Selection 对象的 getRangeAt 方法获取 Range。另外,还可以通过 Document 对象的构造函数 Range() 来得到 Range。
提到range对象我们就不得不提到Section对象,每一个Selection对象都有一个或多个Range对象,每一个Range对象代表用户用鼠标所选取范围内的一段连续区域。

使用场景

通过Range对象,可以获取用户选中的区域,或者指定选中区域,得到Range的起点和终点、修改或者复制里边的文本,甚至是html。在富文本编辑器开发中,经常会使用到这些功能。

CSSOM

  • CSSOM是CSS Object Model的缩写
  • 大体上来说,CSSOM是一个建立在web页面上的 CSS 样式的映射
  • 它和DOM类似,但是只针对CSS而不是HTML
  • 浏览器将DOM和CSSOM结合来渲染web页面

DOM操作的实现case

首先我们进行一个基础的拖拽功能的实现:

<div id="dragable" style="width: 100px; height: 100px; background-color: pink;"></div>
<script>
    let dragable = document.getElementById("dragable");
    dragable.addEventListener("mousedown", function(event){
        let up = () => {
            document.removeEventListener("mousemove", move)
            document.removeEventListener("mouseup", up)
        };

        let move = (event) => {
            console.log(event)
        }
        document.addEventListener("mousemove", move)
        document.addEventListener("mouseup", up)
    })
</script>

在这里,当我们在dragable中点下并且拖拽,就可以监听到这个拖拽的事件,这是一个基础的拖拽骨架代码。
接下来对这个框的位置进行拖拽,优化move的代码逻辑:

<div id="dragable" style="width: 100px; height: 100px; background-color: pink;"></div>
<script>
    let dragable = document.getElementById("dragable");
    //考虑多次拖拽
    let baseX = 0, baseY = 0;

    dragable.addEventListener("mousedown", function(event){
        //拖动的时候减去鼠标最初的位置
        let startX = event.clientX, startY = event.clientY;
        let up = () => {
            baseX = baseX + event.clientX - startX;
            baseY = baseY + event.clientY - startY;
            document.removeEventListener("mousemove", move)
            document.removeEventListener("mouseup", up)
        };

        let move = (event) => {
            dragable.style.transform = `translate(${baseX + event.clientX - startX}px, ${baseY + event.clientY - startY}px)`
        }
        document.addEventListener("mousemove", move)
        document.addEventListener("mouseup", up)
    })
</script>

接下来我们考虑将块拖拽到一堆文字的中间,首先是用range将能够安插的空位寻找出来:

<div id="container">
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
</div>
<div id="dragable" style="width: 100px; height: 100px; background-color: pink;"></div>
<script>
    let dragable = document.getElementById("dragable");
    //考虑多次拖拽
    let baseX = 0, baseY = 0;

    dragable.addEventListener("mousedown", function(event){
        //拖动的时候减去鼠标最初的位置
        let startX = event.clientX, startY = event.clientY;
        let up = () => {
            baseX = baseX + event.clientX - startX;
            baseY = baseY + event.clientY - startY;
            document.removeEventListener("mousemove", move)
            document.removeEventListener("mouseup", up)
        };

        let move = (event) => {
            dragable.style.transform = `translate(${baseX + event.clientX - startX}px, ${baseY + event.clientY - startY}px)`
        }
        document.addEventListener("mousemove", move)
        document.addEventListener("mouseup", up)
    })

    let ranges = [];

    let container = document.getElementById("container");
    for(let i = 0; i < container.childNodes[0].textContent.length; i++) {
        let range = document.createRange();
        range.setStart(container.childNodes[0], i);
        range.setEnd(container.childNodes[0], i);

        console.log(range.getBoundingClientRect())
        ranges.push(range)
    }
</script>

看下打印结果,我们拿到了所有的range的位置:
在这里插入图片描述
定义一个getNearest方法,来找距离当前最近的range:

 function getNearest(x, y){
     let min = Infinity;
     let nearest = null;
     for (let range of ranges) {
         let rect = range.getBoundingClientRect();
         let distance = (rect.x - x) ** 2 + (rect.y - x) ** 2;
         if (distance < min) {
             nearest = range;
             min = distance
         }
     }
     return nearest;
 }

看下打印的结果:
在这里插入图片描述
我们修改之前的move逻辑,就能实现这个拖拽的效果:

<div id="container">
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字 
    文字 文字 文字 文字 文字 文字
</div>
<div id="dragable" style="display: inline-block; width: 100px; height: 100px; background-color: pink;"></div>
<script>
    let dragable = document.getElementById("dragable");
    //考虑多次拖拽
    let baseX = 0, baseY = 0;

    dragable.addEventListener("mousedown", function(event){
        //拖动的时候减去鼠标最初的位置
        let startX = event.clientX, startY = event.clientY;
        let up = () => {
            baseX = baseX + event.clientX - startX;
            baseY = baseY + event.clientY - startY;
            document.removeEventListener("mousemove", move)
            document.removeEventListener("mouseup", up)
        };

        let move = (event) => {
            let range = getNearest(event.clientX, event.clientY);
            range.insertNode(dragable)
            // dragable.style.transform = `translate(${baseX + event.clientX - startX}px, ${baseY + event.clientY - startY}px)`
        }
        document.addEventListener("mousemove", move)
        document.addEventListener("mouseup", up)
    })

    let ranges = [];

    let container = document.getElementById("container");
    for(let i = 0; i < container.childNodes[0].textContent.length; i++) {
        let range = document.createRange();
        range.setStart(container.childNodes[0], i);
        range.setEnd(container.childNodes[0], i);

        console.log(range.getBoundingClientRect())
        ranges.push(range)
    }

    function getNearest(x, y){
        let min = Infinity;
        let nearest = null;
        for (let range of ranges) {
            let rect = range.getBoundingClientRect();
            let distance = (rect.x - x) ** 2 + (rect.y - x) ** 2;
            if (distance < min) {
                nearest = range;
                min = distance
            }
        }
        return nearest;
    }

    document.addEventListener("selectstart", event => event.preventDefault())
</script>

看下效果图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值