dayLog01.自定义拖拽指令引发的基础重学

122 阅读6分钟

欠的账早晚得还

前端fw在写vue拖拽指令时发现自身存在的的一些漏洞。解构出三个问题,js获取dom元素,js获取元素样式,js与css中的元素尺寸,针对以上做一些简单的学习记录。

注意下文内容都要在块级元素与行内元素具备不同性质这个背景下思考

js获取dom元素

常用的获取dom元素的方法我个人分为get方法和selector选择器,共性不谈,都是获取当前元素的指定子元素信息,重点在区别。其中get方法获取到的是动态信息,也就是说,当dom元素发生变化时,在获取元素的js语句不重新执行的情况下,使用get方法得到的元素信息会同时更新,下面是一个经典的例子。

demo之所以会陷入死循环,是因为ul插入新的子元素li后,li_list会自动更新,for循序的判断条件随着li的插入永远得不到满足

//demo
let ul = document.getElementById('el_ul');
let li_list = ul.getElementsByTagName('li');
for(let i = 0; i < li_list.length; i++){
    ul.appendChild(document.createElement('li'));
}

接下来讨论querySelector,这个方法获取到的是静态信息,也就是说,当dom发生变化时,在获取元素的querySelector语句不重新执行的情况下,以获取的元素信息不回发生改变。

//demo
let ul = document.getElementById('el_ul');
let li_list = ul.querySelectorAll('li');
let origin_l = li_list.length;
for(let i = 0; i < li_list.length; i++){
    ul.appendChild(document.createElement('li'));
}
let change_l = li_list.length;
console.log(origin_l) //3
console.log(change_l) //3

总结:将get方法和selector选择器假象成获取视频帧的方法,那么前者的结果跟随播放进度变化,保持当前播放帧,而后者的结果是使用方法时所播放的那一帧。此外,两种方法的常用选项列在下方,供参考

get方法

  • 父级元素.getElementById('元素id'),获取当前元素下匹配的元素对象
  • 父级元素.getElementByClassName('元素class'),获取当前元素下匹配的元素的列表,尽管只有一个元素。通过下标使用元素
  • 父级元素.getElementByTagName('元素标签'),获取当前元素下匹配的元素的列表,尽管只有一个元素。通过下标使用元素

selector选择器

  • 父级元素.querySelector('元素标签/.元素class/#元素id'),返回符合的第一个元素
  • 父级元素.querySelectorAll('元素标签/.元素class/#元素id'),返回符合的全部元素,通过下标使用指定元素

在讨论js获取元素样式之前,先回想一下盒模型以及一些必备的尺寸

js与css中的元素尺寸

在讨论尺寸之前,我们先搞明白盒模型的问题(对应Css属性box-sizing)

标准盒模型 content-box

ie盒模型 border-box

块级元素尺寸结构参考:

外边距(margin)——边框(border)——内边距(padding)——内容(包含实际内容与滚动条宽度)——内边距(padding)——边框(border)——外边距(margin)

css尺寸与js尺寸

  • css width:内容宽度(content-box),即实际内容与滚动条宽度
  • css width:以边框为基准的总宽度(border-box),即边框宽+内边距+内容宽度
  • js offserWidth:边框+内边距+内容总宽度,四舍五入后的整数只读属性
  • js offsetTop:元素边框到父级元素边框的距离
  • js clinetWidth:元素内边距+内容的宽度-滚动条宽度。此属性常用于获取浏览器的视口尺寸注意:内联元素(行内元素)和无样式的元素clientWidth值为0,这里插入一个问题点:行内元素无法通过css设置宽度和高度,仅左右内边距可生效
  • js clinetTop:边框宽度+滚动条宽度

js获取元素信息

在上面内容的基础上,来讨论js获取元素信息的问题,根据获取的信息不同,分为如下几种方法

1>.style属性

注:在使用.style方法之前需要明确,这是一个即可读又可写的api,但是它所读取的属性仅限于内联样式,即写在标签style属性中的样式,且获取到的结果以字符串形式体现。

因此,通常使用这个方法给元素动态添加样式,至于获取相关样式信息,往往采用其他方法。

<div id="box" style="width:200px;"></div>
let box = getElementById('box');
console.log(box.style.width)//200px;
box.style.height="200px";
box.style.border= "1px solid #abc";

2>偏移尺寸offset\客户端尺寸clinet\滚动尺寸scroll

注:这三者的值均为数值类型,单位为px,且仅可在元素渲染的前提下获取,即display不为none。其中前两者是只读属性,且每次访问都会重新计算,要考虑性能问题。

offset使用总结

获取元素相对父级的位置:.offsetTop /.offsetLeft,这两个值在不存在定位关系的结构里,影响因子只有父级的内边距和自身的外边距(取和),当存在定位关系时,相对位置将以与当前元素距离最近的父级相对定位元素为参照,也就是说,当前元素存在postion:absolute属性,若其父级元素都未设置relative,则以body标签为参照,数值为定位top/left值与外边距之和

获取元素的宽高:offsetWidth/offsetHeight,这两个值对应的是ie盒模型的元素尺寸,也是标准盒模型下元素实际填充的尺寸,也就是拖拽元素时有效的触碰范围,但并不一定是css属性中的width值。

client使用总结

获取当前视口的宽高:document.documentElement.clinetWidth/.Height,当前视口即网页内容的可视区域的大小,不包括滚动条

获取鼠标位置:e.clintX\Y,在鼠标点击事件中使用这两个属性获取鼠标当前相对于网页可视区域的坐标,以内容区域左上角为原点。注意与e.pageX\Y的区别,page的范围是html文档,原点在文档的左上角,也是就是说,当页面发生滚动时,这两个api的结果将产生差异,且后者不兼容ie

scroll使用总结

获取元素内容宽高:.scrollHeight/Width,注意:这里的内容指的是当前元素的内边距以及内部元素的完整高度——即前文尺寸结构所示

隐藏像素数/滚动到指定位置:.scrollLeft/Top,这两个属性同时可读写,读取获得当前元素scrollHeight/width中不可见区域的像素数,主动写入时,控制当前元素在有效范围内沿当前方向滚动指定像素数。

3>getBoundingClientRect()

此方法兼容性很好,有6个属性

  • width\height:元素相对于ie盒模型的宽高
  • left\right\top\bottom:元素以自身边框为有效区域边界,相对于父级/最近定位父级的位置。注意:bottom/right不是元素距离父级下边界和右边界的距离,而是,元素底部边界和右边界相对于外层上方和左侧的距离,也即bottom = top + height。此方法位置参照始终为外层的上边界与左边界

4>js获取元素css属性值

在使用外部样式或文档样式时,以上的方法无法直接获取的元素身上的css属性值,针对这一问题,继续讨论两个api,分别是document.defaultView.getComputedStyle()(非ie),currentStyle(ie)

鉴于ie已经被废弃,不再对其进行额外说明

defaultView.getComputedStyle()

该方法用于在非ie的浏览器环境下获取元素身上全部的样式,并且通过第二次参数获取伪元素的样式信息,得到的结果均为字符串,且尺寸属性为最终转化为px的值,颜色为对应rgba值

<style>
#box1{
    width:200px;
    height:50%;
    padding:20px;
    margin:10px;
    border:5px solid #ccc;
    background:#abc;
    font-size:0.3rem;
}
</style>
<div id="box1"></div>
let box1 = document.getElementById('box1');
let box1_style = document.defaultView.getComputedStyle(box1,null)
console.log('width:',box1_style.width) //200px
console.log('width:',box1_style.height) //510.8px
console.log('width:',box1_style.padding) //30px
console.log('width:',box1_style.backgroundColor) //rgb(85, 136, 153)
console.log('width:',box1_style.backgroundColor) //5px solid rgb(204, 204, 204)
console.log('width:',box1_style.fontSize) //12px