拖拽的相关事项,以及常见dom操作总结

308 阅读6分钟

1、点击事件获取的信息:

①offsetX,offsetY 元素坐标系

点击位置到所在的dom元素的外边框的距离

image.png

②offsetTop,offsetLeft

点击位置所在dom元素的left和top

image.png

③clientX,clientY 浏览器坐标系

点击位置到浏览器视口的坐标(不会计算滚动条)

④pageX,pageY 文档坐标系

点击位置到document的边界的坐标(会计算滚动条距离)

⑤screenX,screenY 显示器坐标系

点击位置到屏幕边界的坐标

2、拖拽事件

js原生实现拖拽原理:

mouseDown -》mounseover移入 -》 mousemove移动 -》mouseEnter移出

通过点击开始拖动和释放结束拖动两个事件能获取的信息,计算出dom变化后的left和top,并且修改。

·点击时记录offsetX,offsetY(当前点击位置到达拖动的dom的外边框的距离)

松手时记录此时的pageX,pageY。然后把此时的pageX - 刚刚记录的offsetX,得到移动后的left。top同理。

·html5拖拽:

dragstart:鼠标点击并且开始拖动(相当于mousedown+mousemove的组合),此时这个拖动元素会触发该事件。

②dragenter:鼠标正在拖动元素,并且鼠标进入某个dom(目标元素) ,此时这个dom会触发该事件。

③dragover:鼠标在其它dom元素(目标元素)身上移动,此时这个dom会频繁触发该事件。

④dragleave:鼠标正在拖动元素,并且鼠标移出某个dom(目标元素),此时这个dom会触发该事件。

⑤drop:正拖拽元素,并且松开鼠标

drag:拖动过程中,被拖动的元素频繁触发

dragend:拖动结束后,被拖动的元素触发

加粗的是被拖动元素的事件。没加粗的是目标元素的事件。

drop之前一定会有dragover,所以通常用dragover保护drop。并且在dragover中需要移除默认行为,因为div的浏览器默认行为就是不允许drop的。

image.png

e.dataTransfer对象

用于装载移动资源,在dragstart的时候进行设置资源,在drop的时候取出。

image.png

3、原生实现拖拽排序

<template>
  <div
    class="wrap"
    @dragstart="handleDragStart"
    @dragover="handleDragOver"
    @dragend="drop"
  >
    <div class="item" draggable>1</div>
    <div class="item" draggable>2</div>
    <div class="item" draggable>3</div>
    <div class="item" draggable>4</div>
    <div class="item" draggable>5</div>
    <div class="item" draggable>6</div>
    <div class="item" draggable>7</div>
    <div class="item" draggable>8</div>
  </div>
</template>

<script>
export default {
  name: "DragSort",
  data: () => {
    return {
      currentDrag: null,
    };
  },
  mounted() {},
  methods: {
    drop: (e) => {
      e.target.classList.remove("drag");
    },
    handleDragStart(e) {
      this.currentDrag = e.target;
        //不然拖拽的样式会生效到拖动元素身上!
      setTimeout(() => {
        this.currentDrag.classList.add("drag");
      }, 50);
    },
    handleDragOver(e) {
      e.preventDefault();
      if (
        this.currentDrag == e.target ||
        !e.target?.classList?.contains("item")
      ) {
        return;
      }
      const target = e.target;
      //获取元素的索引,先把dom的List转化成数组,然后找到对应的两个元素的索引
      const list = Array.from(document.querySelector(".wrap").children);
      const targetIndex = list.indexOf(target);
      const dragIndex = list.indexOf(this.currentDrag);
      //交换元素的位置,向上和向下拖拽的逻辑是不同的。根据index判断是向上还是向下
      if (targetIndex > dragIndex) {
        let wrap = document.querySelector(".wrap");
        wrap.insertBefore(this.currentDrag, target.nextSibling);
      } else {
        let wrap = document.querySelector(".wrap");
        wrap.insertBefore(this.currentDrag, target);
      }
    },
  },
};
</script>

<style>
.wrap {
  display: grid;
  grid-template-columns: 1fr;
  margin: 0 auto;
  height: 340px;
  width: 450px;
  border: 1px solid red;
  border-radius: 6px;
}
.item {
  box-sizing: border-box;
  overflow: hidden;
  border-radius: 5px;
  background-color: pink;
  margin-bottom: 5px;

  text-align: center;
}
.drag {
  border: 1px dashed #000;
  background-color: transparent;
}
</style>

4、dom操作

①元素操作

1. 根据 ID 名
   document.getElementById()
   
2. 根据标签名
   document.getElementsByTagName()   返回 HTMLCollection 对象
   元素对象.getElementsByTagName()
   
3. 根据类名
   document.getElementsByClassName()  返回 HTMLCollection 对象
   元素对象.getElementsByClassName()
   
4. 根据name属性值
   document.getElementsByName()     返回 NodeList 对象
   
5. 使用 CSS 选择器
   document.querySelector();        返回第一个满足条件的元素
   document.querySelectorAll()      返回NodeList对象
   元素对象.querySelector();
   元素对象.querySelectorAll();
   
6. 获取所有的元素
   document.all
​

② 根据元素关系获取元素(文档关系)

节点树:
childNodes
firstChild
lastChild
parentNode
previousSibling
nextSibling
​
​
元素树:
children
firstElementChild
lastElementChild
parentElement
previousElementSibling
nextElementSibling

③ 操作元素的属性

1) 读写内置属性
   元素对象.属性名;
   元素对象['属性名'];
​
2) 读写自定义属性(写在标签上的属性)
   元素对象.getAttribute('属性名');
   元素对象.setAttribute('属性名', '值');
​
​
3) 读写data-格式的自定义属性
   元素对象.dataset.属性名;

读写行内样式 - style

// 设置行内样式
元素对象.style.color = 'red';
元素对象.style.backgroudColor = 'red';
元素对象.style['background-color'] = 'red';
​
// 读取行内样式
元素对象.style.color;
元素对象.style.backgroudColor;
元素对象.style['background-color'];

通过设置元素的类名操作样式 - classList

① className

HTML 标签中的 class 属性对应 js 元素对象的 className 属性,是内置属性
​
元素对象.className;  // 可读可写

② classList

classList 是一个包含了该元素所有类名的伪数组,属性和方法如下:
​
元素对象.classList.length       类名个数
元素对象.classList.add()        添加一个类名
元素对象.classList.remove()     删除一个类名
元素对象.classList.toggle()     切换一个类名(元素有该类名删除,无该类名添加) 

3 读写元素的文本内容(可读可写)

元素对象.innerHTML          读写元素内部的HTML内容
元素对象.outerHTML          读写包括本元素在内的HTML内容
元素对象.innerText          读写元素内部的文本内容(去除HTML标签,不保留缩进格式)
元素对象.textContent        读写元素内部的文本内容(去除HTML标签,保留缩进格式)

注意: 只有双标签元素才有可能具有文本内容!

4 读取元素的尺寸(只读)

offsetWidth、offsetHeight        获取元素整体的宽高(内容+内边距+ 边框)
clientWidth、clientHeight        获取元素除去边框外的宽高(内容+内边距)
scrollWidth、scrollHeight        client的基础上+溢出部分的宽高 (没有溢出内容同 client系列)
​
getBoundingClientRect()         返回一个对象,对象中有 widthheight 属性,同 offset 系列

注意: 以上方式只能读取元素的宽高,如果要设置元素的宽高用CSS样式设置。

获取视口的宽高:

// 1 方式一
window.innerWidth;
window.innerHeight;
​
// 2 方式二   与方式一相比,不会把滚动条本身的宽度算进来
document.documentElement.clientWidth;
document.documentElement.clientHeight;

5 读取元素的位置 (只读)

offsetLeft、offsetTop    获取元素在第一个定位的祖先元素上的位置(如果没有定位的祖先元素,参考整个文档)
clientLeft、clientTop    获取左边框宽度、上边框宽度
​
getBoundingClientRect()  返回一个对象,具有如下属性:
                         lefttop       获取元素左上角在视口中的坐标
                         x、y            同 lefttop
                         rightbottom   获取元素右边框在视口的位置、下边框在视口位置
 

注意: 以上方式只能读取元素的位置,如果要设置元素的位置用CSS样式设置。

rectDom.right - rectDom.left === rectDom.width;
rectDom.bottom-rectDom.top === rectDom.height;

6 读写元素中内容的位置(可读可写)

scrollLeft      读取或设置元素中内容的水平位置
scrollTop       读取或设置元素中内容的垂直位置

设置元素中内容的位置,有两个前提:

① 元素中的内容要溢出。

② 元素的 CSS 属性 overflow 不能设置为 visible,可以是 auto、hidden、scroll。

读取或设置页面在视口中的位置:

// 非IE
document.documentElement.scrollLeft
document.documentElement.scrollTop// IE
document.body.scrollLeft
document.body.scrollTop// 兼容性获取页面滚动距离的方式
document.documentElement.scrollTop || document.body.scrollTop;