dom-style

元素的宽高

gcs() cs 返回的只是元素内容区高度

因为没有 css 属性时描述内容区 + 内边距的,即是转换成了 border-box

Element.clientHeight,Element.clientWidth

Element.clientHeight 属性返回一个整数值,表示元素节点的 CSS 高度(单位像素),只对块级元素生效,对于行内元素返回 0。如果块级元素没有设置 CSS 高度,则返回实际高度。 @@@

除了元素本身的高度,它还包括 padding 部分,但是不包括 bordermargin。如果有水平滚动条,还要减去水平滚动条的高度。

注意,这个值始终是整数,如果是小数会被四舍五入。

Element.offsetHeight,Element.offsetWidth

Element.offsetHeight 属性返回一个整数,表示元素的 CSS 垂直高度(单位像素),包括元素本身的高度、padding 和 border,以及水平滚动条的高度(如果存在滚动条)。

Element.offsetWidth 属性表示元素的 CSS 水平宽度(单位像素),其他都与 Element.offsetHeight 一致。

这两个属性都是只读属性,只比 Element.clientHeightElement.clientWidth 多了边框的高度或宽度。如果元素的 CSS 设为不可见(比如 display: none;),则返回 0

注意:虽然是 offset 系列,但是却不是定位相关的属性。进行偏移的时候移动的 可见整体,不包括滚动条

视口

document.documentElementclientHeight 属性,返回当前视口的高度(即浏览器窗口的高度),等同于 window.innerHeight 属性减去水平滚动条的高度(如果有的话)。

document.body 的高度则是网页的实际高度。一般来说,document.body.clientHeight 大于 document.documentElement.clientHeight

// 视口高度
document.documentElement.clientHeight

// 网页总高度,考虑了垂直滚动,相当于是可滚动范围
document.body.clientHeight
// 获取视口的尺寸
var w = document.documentElement.clientWidth;
var w2 = document.documentElement.offsetWidth;
console.log(w,w2); // 任何时候都是一样的,不包括滚动条,视口的宽高,视口的padding和border都是往内缩的,可以理解为视口默认是border-box

对于其他元素而言,正常遵循 API 的特性

注意:在 ie10 及 ie10 以下,根标签的 clientWidth 和 offsetWidth 和普通的一样

mobile-basic

Element.scrollHeight,Element.scrollWidth

Element.scrollHeight 属性返回一个整数值(小数会四舍五入),表示当前元素的总高度(单位像素),包括溢出容器、当前不可见的部分。它包括 padding,还包括伪元素(::before::after)的高度。但是不包括 bordermargin 以及水平滚动条的高度,

Element.scrollWidth 属性表示当前元素的总宽度(单位像素),其他地方都与 scrollHeight 属性类似。这两个属性只读。

整张网页的总高度可以从 document.documentElementdocument.body 上读取。

// 返回网页的总高度
document.documentElement.scrollHeight
document.body.scrollHeight

Element.clientLeft,Element.clientTop

Element.clientLeft 属性等于节点左边框(left border)的宽度(单位像素),不包括左侧的 paddingmargin。如果没有设置左边框,或者是行内元素(display: inline),该属性返回 0。该属性总是返回整数值,如果是小数,会四舍五入。

Element.clientTop 属性等于节点顶部边框的宽度(单位像素),其他特点都与 clientLeft 相同。

提示: 你可以使用 style.borderTopWidth 属性来获取元素顶部框的宽度。

提示: 要返回元素左侧边框的宽度可以使用 clientLeft 属性。

元素的位置

Element.scrollLeft,Element.scrollTop

Element.scrollLeft 属性表示当前元素的水平滚动条向右侧滚动的像素数量

Element.scrollTop 属性表示当前元素的垂直滚动条向下滚动的像素数量

对于那些没有滚动条的网页元素这两个属性总是等于 0。

如果要查看整张网页的水平的和垂直的滚动距离,要从 document.documentElement 元素上读取。

  document.documentElement.scrollLeft
  document.documentElement.scrollTop

这两个属性都可读写,设置该属性的值,会导致浏览器将当前元素自动滚动到相应的位置。

注意:不用加 px 单位

判断滚动条是否滚动到底

Element.offsetParent

Element.offsetParent 属性返回最靠近当前元素的、并且 CSS 的 position 属性不等于 static 的上层元素。

该属性主要用于确定子元素位置偏移的计算基准,Element.offsetTopElement.offsetLeft 就是 offsetParent 元素计算的。

如果该元素是不可见的(display 属性为 none),或者位置是固定的(position 属性为 fixed),则 offsetParent 属性返回 null

如果某个元素的所有上层节点的 position 属性都是 static,则 Element.offsetParent 属性指向 <body> 元素。

保证:html 和 body 之间的 margin 被清除

本身定位为 fiexd,不管你父级有没有定位:

非 fiexd(包括本身不定位):

body 的 offsetparent:null,就算 html、body 的 margin 没有 reset,也不影响,offsetLeftTop 的值都是 0

区别 parentNode 和 offsetParent

Element.offsetLeft,Element.offsetTop

Element.offsetLeft 返回当前元素左上角相对于 Element.offsetParent 节点的水平位移,element.offsetTop` 返回垂直位移,单位为像素。

通常,这两个值是指相对于父节点 内边距边界 的位移。

没有开启定位的元素也有这两个属性

获取元素的绝对位置

下面的代码可以算出元素左上角相对于整张网页的坐标。

function getElementPosition(e) {
  var x = 0;
  var y = 0;
  while (e !== null)  {
    x += e.offsetLeft;
    y += e.offsetTop;
    e = e.offsetParent;
  }
  return {x: x, y: y};
}

首先 html 和 body 的 margin 要重置,因为循环最终只能去到 body

boder margin 会影响这个函数的取值

1548123244295

offsetLeftTop 会直接无视 border 的值

自己和父元素的 内边距 不会对 offsetLeftTop 造成影响,因为左上角始终不变,内容区被挤向右下方而已

1548124728065

自己的外边距、父元素的外边距 会影响 offsetLeftTop 的值,因为从内边距边界出发

Element.getBoundingClientRect()

Element.getBoundingClientRect 方法返回一个对象,提供当前元素节点的大小、位置等信息,基本上就是 CSS 盒状模型的所有信息。

getBoundingClientRect 方法返回的 rect 对象,具有以下属性(全部为只读)。

注意,getBoundingClientRect 方法的所有属性,都把边框(border 属性)算作元素的一部分。也就是说,都是从边框外缘的各个点来计算。因此,widthheight 包括了元素本身 + padding + border

另外,上面的这些属性,都是继承自原型的属性,Object.keys 会返回一个空数组,这一点也需要注意。

var rect = document.body.getBoundingClientRect();
Object.keys(rect) // []

上面代码中,rect 对象没有自身属性,而 Object.keys 方法只返回对象自身的属性,所以返回了一个空数组。

获取元素的相对位置(到视口)

绝对位置减去元素滚动的距离

  function getPointRe(node){
    //while循环叠加offsetParent的offsetTop和offsetLeft
    var x =0;
    var y = 0;
    while(node){
      x+=node.offsetLeft;
      y+=node.offsetTop;
  
      node = node.offsetParent;
    }
  
    var L = document.documentElement.scrollLeft||document.body.scrollLeft;
    var T = document.documentElement.scrollTop||document.body.scrollTop;
  
    return {x:x-L,y:y-T};
  }

getBoundingClientRect

一个元素四个角的相对位置

getBoundingClientRect + 滚动条滚动时元素滚动的距离→绝对位置

  /*
  代表元素border-box的尺寸(可以使用offsetWH代替)
  height
  width
  
  元素左上角的相对位置
  left
  top
  
  元素右下角的相对位置
  right
  bottom
  */
  window.onload=function(){
    var inner1 = document.querySelector("#inner1");
    var point = inner1.getBoundingClientRect();
    console.log(point);
  }

getBoundingClientRect() 方法。它返回一个对象,其中包含了 left、right、top、bottom 四个属性,分别对应了该元素的左上角和右下角相对于浏览器窗口(viewport)左上角的距离。

Demo Mac 停靠栏

  var r = 320;
  var imgNodes = document.querySelectorAll("#wrap > img");
  
  document.onmousemove=function(ev){
    ev =  ev||event;
    for(var i=0;i<imgNodes.length;i++){
      var a = imgNodes[i].getBoundingClientRect().left + imgNodes[i].offsetWidth/2 - ev.clientX;
      var b = imgNodes[i].getBoundingClientRect().top + imgNodes[i].offsetHeight/2 - ev.clientY;
      var c = Math.sqrt(a*a+b*b);
  
  
      if(c>=r){
        c=r;
      }
      imgNodes[i].style.width =128 - c*0.2 +"px";
    }
  }

元素距离左上角的距离

下面的代码可以算出元素左上角相对于整张网页的坐标。

function getElementPosition(e) {
  var x = 0;
  var y = 0;
  while (e !== null)  {
    x += e.offsetLeft;
    y += e.offsetTop;
    e = e.offsetParent;
  }
  return {x: x, y: y};
}

判断滚动条是否滚动到底

垂直滚动条:scrollHeight - scrollTop = clientHeight

水平滚动:scrollWidth - scrollLeft = clientWidth

容器内拖拽的最长距离

拖拽的最长距离:容器的 clientWidth - 子元素的 offsetWidth

在容器的内容区移动,子元素的边框不会被覆盖

其他样式相关的 API

Element.matches()

Element.matches 方法返回一个布尔值,表示当前元素是否匹配给定的 CSS 选择器。

  if (el.matches('.someClass')) {
    console.log('Match!');
  }

内联样式

操作样式最简单的方法,就是使用网页元素节点的 getAttribute 方法、setAttribute 方法和 removeAttribute 方法,直接读写或删除网页元素的 style 属性来操作元素的 内联样式

div.setAttribute(
  'style',
  'background-color:red;' + 'border:1px solid black;'
);

上面的代码相当于下面的 HTML 代码。

<div style="background-color:red; border:1px solid black;" />

style 不仅可以使用字符串读写,它本身还是一个对象,部署了 CSSStyleDeclaration 接口(详见下面的介绍),可以直接读写个别属性。

e.style.fontSize = '18px';
e.style.color = 'black';

通过 style 修改的样式都是内联样式,由于内联样式的优先级比较高,所以我们通过 JS 来修改的样式,往往会立即生效,但是如果样式中设置了!important,则内联样式将不会生效。

重置样式 样式表 的冲突

// 如果把宽度设置为0,那么内联样式的特殊性只能在样式表通过!important解决
// upNodes[i].style.width="0";
// 把值设为空,在内联样式不会有属性值,样式表的属性就可以正常作用了
upNodes[i].style.width="";

单位

.style.property 需要加 px,如果是 attribute 浏览器会自动加 px

总结

:style 属性

写单个:style 属性

写多个

多个覆盖

CSSStyleDeclaration 接口

简介

CSSStyleDeclaration 接口用来操作元素的样式。三个地方部署了这个接口。

CSSStyleDeclaration 接口可以直接读写 CSS 的样式属性,不过,连词号需要变成骆驼拼写法。

var divStyle = document.querySelector('div').style;

divStyle.backgroundColor = 'red';
divStyle.border = '1px solid black';
divStyle.width = '100px';
divStyle.height = '100px';
divStyle.fontSize = '10em';

divStyle.backgroundColor // red
divStyle.border // 1px solid black
divStyle.height // 100px
divStyle.width // 100px

上面代码中,style 属性的值是一个 CSSStyleDeclaration 实例。这个对象所包含的属性与 CSS 规则一一对应。

注意

CSSStyleDeclaration 实例属性

cssText

Length

parentRule

CSSStyleDeclaration 实例方法

getPropertyPriority()

getPropertyValue()

item()

removeProperty()

setProperty()

CSS CanIUse

CSS 的规格发展太快,新的模块层出不穷。不同浏览器的不同版本,对 CSS 模块的支持情况都不一样。有时候,需要知道当前浏览器是否支持某个模块,这就叫做“CSS 模块的侦测”。

一个比较普遍适用的方法是,判断元素的 style 对象的某个属性值是否为字符串。

  typeof element.style.animationName === 'string';
  typeof element.style.transform === 'string';

如果该 CSS 属性确实存在,会返回一个字符串。即使该属性实际上并未设置,也会返回一个空字符串。如果该属性不存在,则会返回 undefined

  document.body.style['maxWidth'] // ""
  document.body.style['maximumWidth'] // undefined

上面代码说明,这个浏览器支持 max-width 属性,但是不支持 maximum-width 属性。

注意,不管 CSS 属性名的写法带不带连词线,style 属性上都能反映出该属性是否存在。

  document.body.style['backgroundColor'] // ""
  document.body.style['background-color'] // ""

另外,使用的时候,需要把不同浏览器的 CSS 前缀也考虑进去。

  var content = document.getElementById('content');
  typeof content.style['webkitAnimation'] === 'string'

这种侦测方法可以写成一个函数。

  function isPropertySupported(property) {
    if (property in document.body.style) return true;
    var prefixes = ['Moz', 'Webkit', 'O', 'ms', 'Khtml'];
    var prefProperty = property.charAt(0).toUpperCase() + property.substr(1);
  
    for(var i = 0; i < prefixes.length; i++){
      if((prefixes[i] + prefProperty) in document.body.style) return true;
    }
  
    return false;
  }
  
  isPropertySupported('background-clip')
  // true

dom-style

CSS 对象

浏览器原生提供 CSS 对象,为 JavaScript 操作 CSS 提供一些工具方法。

这个对象目前有两个静态方法。

CSS.escape()

CSS.escape 方法用于转义 CSS 选择器里面的特殊字符。

  <div id="foo#bar">

上面代码中,该元素的 id 属性包含一个 # 号,该字符在 CSS 选择器里面有特殊含义。不能直接写成 document.querySelector('#foo#bar'),只能写成 document.querySelector('#foo\\#bar')。这里必须使用双斜杠的原因是,单引号字符串本身会转义一次斜杠。

CSS.escape 方法就用来转义那些特殊字符。

  document.querySelector('#' + CSS.escape('foo#bar'))

CSS.supports()

CSS.supports 方法返回一个布尔值,表示当前环境是否支持某一句 CSS 规则。

它的参数有两种写法,一种是第一个参数是属性名,第二个参数是属性值;另一种是整个参数就是一行完整的 CSS 语句。

  // 第一种写法
  CSS.supports('transform-origin', '5px') // true
  
  // 第二种写法
  CSS.supports('display: table-cell') // true

注意,第二种写法的参数结尾不能带有分号,否则结果不准确。

  CSS.supports('display: table-cell;') // false

window.getComputedStyle(node)

行内样式(inline style)具有最高的优先级,改变行内样式,通常会立即反映出来。但是,网页元素最终的样式是综合各种规则计算出来的。因此,如果想得到元素实际的样式,只读取行内样式是不够的,需要得到浏览器最终计算出来的样式规则。

window.getComputedStyle 方法,就用来返回浏览器计算后得到的最终规则。它接受一个节点对象作为参数,返回一个 CSSStyleDeclaration 实例,包含了指定节点的最终样式信息。所谓“最终样式信息”,指的是各种 CSS 规则叠加后的结果。

注意,CSSStyleDeclaration 实例是一个 活的对象,任何对于样式的修改,会实时反映到这个实例上面。另外,这个实例是只读的。

getComputedStyle 方法还可以接受第二个参数,表示当前元素的伪元素(比如 :before:after:first-line:first-letter 等)。

  var result = window.getComputedStyle(div, ':before');

有几点需要注意:

IE8

使用 currentStyle 属性

语法:元素.currentStyle.样式名

box.currentStyle["width"] //[]字面量形式兼容性好
width:auto;

通过这个属性读取到的样式是只读的不能修改

只要判断有没有这个属性,即可实现兼容

window.getComputedStyle 有的话返回,然后转换为 true,没有的话 undefined,转换为 flase 。因为 IE 高版本两者都有,建议用 gcs

StyleSheet 接口

概述

StyleSheet 接口代表网页的一张样式表,包括 <link> 元素加载的样式表和 <style> 元素内嵌的样式表。

document 对象的 styleSheets 属性,可以返回当前页面的所有 StyleSheet 实例(即所有样式表)。它是一个类似数组的对象。

如果是 <style> 元素嵌入的样式表,还有另一种获取 StyleSheet 实例的方法,就是这个节点元素的 sheet 属性。

// HTML 代码为 <style id="myStyle"></style>
var myStyleSheet = document.getElementById('myStyle').sheet;
myStyleSheet instanceof StyleSheet // true

严格地说,StyleSheet 接口不仅包括网页样式表,还包括 XML 文档的样式表。所以,它有一个子类 CSSStyleSheet 表示网页的 CSS 样式表。我们在网页里面拿到的样式表实例,实际上是 CSSStyleSheet 的实例。这个子接口继承了 StyleSheet 的所有属性和方法,并且定义了几个自己的属性,下面把这两个接口放在一起介绍。

实例属性

StyleSheet.disabled

Stylesheet.href: 返回样式表的网址。对于内嵌样式表,该属性返回 null。该属性只读。

StyleSheet.media

document.styleSheets[0].media.appendMedium('handheld');
document.styleSheets[0].media.deleteMedium('print');

StyleSheet.title

StyleSheet.type: 属性返回样式表的 type 属性,通常是 text/css

StyleSheet.parentStyleSheet: CSS 的 @import 命令允许在样式表中加载其他样式表。StyleSheet.parentStyleSheet 属性返回包含了当前样式表的那张样式表。如果当前样式表是顶层样式表,则该属性返回 null

StyleSheet.ownerNode: StyleSheet.ownerNode 属性返回 StyleSheet 对象所在的 DOM 节点,通常是 <link><style>。对于那些由其他样式表引用的样式表,该属性为 null

CSSStyleSheet.cssRules: 属性指向一个类似数组的对象(CSSRuleList 实例),里面每一个成员就是当前样式表的一条 CSS 规则。使用该规则的 cssText 属性,可以得到 CSS 规则对应的字符串。

var sheet = document.querySelector('#styleElement').sheet;

sheet.cssRules[0].cssText
// "body { background-color: red; margin: 20px; }"

sheet.cssRules[1].cssText
// "p { line-height: 1.4em; color: blue; }"

每条 CSS 规则还有一个 style 属性,指向一个对象,用来读写具体的 CSS 命令。

  cssStyleSheet.cssRules[0].style.color = 'red';
  cssStyleSheet.cssRules[1].style.color = 'purple';

CSSStyleSheet.ownerRule

实例方法

CSSStyleSheet.insertRule()

CSSStyleSheet.insertRule 方法用于在当前样式表的插入一个新的 CSS 规则。

var sheet = document.querySelector('#styleElement').sheet;
sheet.insertRule('#block { color: white }', 0);
sheet.insertRule('p { color: red }', 1);

该方法可以接受两个参数,第一个参数是表示 CSS 规则的字符串,这里只能有一条规则,否则会报错。第二个参数是该规则在样式表的插入位置(从 0 开始),该参数可选,默认为 0(即默认插在样式表的头部)。注意,如果插入位置大于现有规则的数目,会报错。

该方法的返回值是新插入规则的位置序号。

注意,浏览器对脚本在样式表里面插入规则有很多 限制。所以,这个方法最好放在 try...catch 里使用。

CSSStyleSheet.deleteRule()

CSSStyleSheet.deleteRule 方法用来在样式表里面移除一条规则,它的参数是该条规则在 cssRules 对象中的位置。该方法没有返回值。

document.styleSheets[0].deleteRule(1);

实例:添加样式表

网页添加样式表有两种方式。一种是添加一张内置样式表,即在文档中添加一个 <style> 节点。

另一种是添加外部样式表,即在文档中添加一个 <link> 节点,然后将 href 属性指向外部样式表的 URL。

CSSRule 接口

#

CSSRuleList 接口

#

window.matchMedia()

#

基本用法

window.matchMedia 方法用来将 CSS 的 MediaQuery 条件语句,转换成一个 MediaQueryList 实例。

var mdl = window.matchMedia('(min-width: 400px)');
mdl instanceof MediaQueryList // true

注意,如果参数不是有效的 MediaQuery 条件语句,window.matchMedia 不会报错,依然返回一个 MediaQueryList 实例。

window.matchMedia('bad string') instanceof MediaQueryList // true

MediaQueryList 接口的实例属性

MediaQueryList.media

MediaQueryList.media 属性返回一个字符串,表示对应的 MediaQuery 条件语句。

MediaQueryList.matches

MediaQueryList.matches 属性返回一个布尔值,表示当前页面是否符合指定的 MediaQuery 条件语句。

if (window.matchMedia('(min-width: 400px)').matches) {
/* 当前视口不小于 400 像素 */
} else {
/* 当前视口小于 400 像素 */
}

下面的例子根据 mediaQuery 是否匹配当前环境,加载相应的 CSS 样式表。

var result = window.matchMedia("(max-width: 700px)");

if (result.matches){
var linkElm = document.createElement('link');
linkElm.setAttribute('rel', 'stylesheet');
linkElm.setAttribute('type', 'text/css');
linkElm.setAttribute('href', 'small.css');

document.head.appendChild(linkElm);
}

MediaQueryList.onchange

如果 MediaQuery 条件语句的适配环境发生变化,会触发 change 事件。MediaQueryList.onchange 属性用来指定 change 事件的监听函数。该函数的参数是 change 事件对象(MediaQueryListEvent 实例),该对象与 MediaQueryList 实例类似,也有 mediamatches 属性。

var mql = window.matchMedia('(max-width: 600px)');

mql.onchange = function(e) {
if (e.matches) {
  /* 视口不超过 600 像素 */
} else {
  /* 视口超过 600 像素 */
}
}

上面代码中,change 事件发生后,存在两种可能。一种是显示宽度从 700 像素以上变为以下,另一种是从 700 像素以下变为以上,所以在监听函数内部要判断一下当前是哪一种情况。

MediaQueryList 接口的实例方法

MediaQueryList 实例有两个方法 MediaQueryList.addListener()MediaQueryList.removeListener(),用来为 change 事件添加或撤销监听函数。

  var mql = window.matchMedia('(max-width: 600px)');
  
  // 指定监听函数
  mql.addListener(mqCallback);
  
  // 撤销监听函数
  mql.removeListener(mqCallback);
  
  function mqCallback(e) {
    if (e.matches) {
      /* 视口不超过 600 像素 */
    } else {
      /* 视口超过 600 像素 */
    }
  }

FAQ

#faq/js

获取元素到视口相对位置

获取元素的相对位置(到视口)

判断滚动条是否滚动到底

判断滚动条是否滚动到底