Go to comments

JavaScript 获取窗口属性 dom尺寸

这部分知识属于查缺补漏,可以放松放松注意力,因为这块属于理解了就可以,然后把它封装到一个库里面以后用直接调就可以(面试没人问、考试也没人考)听懂了掌握了会封装方法,以后用的时候直接调就可以,

这块内容基本是一个概念,求屏幕尺寸的宽和高、求滚动条在哪里、求滚动条X轴和Y轴的坐标,都是边边角角的东西不是那么难


一、滚动条的滚动距离


因为有兼容的问题,滚动条有很多种求的方法

1、3WC标准方法

PS:

IE9是一个非常尴尬的浏览器,IE9以上完全支持W3C标准,IE9以下没有支持W3C标准(部分不支持),IE9支持了一小点但是更多的是没支持,

拿IE9作为一个分界线,IE9以下没有特别好的遵循W3C标准,IE9以上支持了,IE9是一个过渡版本


这是window上的两个属性,这两个属性就是求滚动条滚动距离的,IE9以上的浏览器能用,IE8及IE8以下浏览器不兼容


 window.pageYOffset  现在滚动条没滚动,返回的是0

image.png


把滚动条往下挪了一下,再调用 window.pageYOffset,返回的数值是527,数值单位是像素

image.png


问一个问题:

现在滚动条往下滚动了400像素,从 浏览器最顶端 到滚动条 滚动的位置 一共有多少个像素?

要400像素 + 首屏像素,因为滚动条滚动的距离就相对于多挪出来的距离

image.png

换句话应该这么说,滚动条滚动了400像素,此时屏幕的低端 距离 整个网页的最顶端 有多少像素?400像素 + 首屏像素 这是一个以后会经常求的问题


元素的宽度超过屏幕的宽度,肯定就有横向的滚动条了

<hr style="width:1000px;">

 window.pageXOffset  挪动横向滚动条,返回数值225,是横向滚动条滚动的距离

image.png


2、IE8及IE8下用的方法

IE8及IE8以下浏览器支持两个方法


第一个

 document.body.scrollLeft  横向滚动条的距离(和标准方法window.pageXOffset结果是一样的)

 document.body.scrollTop  纵向滚动条的距离

第二个

document.documentElement.scrollLeft

document.documentElement.scrollTop( documentElement代表html )


这块有一个兼容性混乱的问题

document.body.scrollLeft/scrollTop方法能用

document.documentElement.scrollLeft/scrollTop方法就用不了


这两个方法到底在哪个浏览器上好用是没有定论的,

这两个方法有个规律是互相冲突,在任何一个浏览器上但凡 document.body.scrollLeft/Top有值,document.documentElement.scrollLeft/scrollTop 一定是0


所以它两最好的用法是相加

document.body.scrollLeft + document.documentElement.scrollLeft;

document.body.scrollTop + document.documentElement.scrollTop;

求滚动条滚动的距离,明明一个方法就能搞定,由于兼容性出现了一大堆问题

image.png


3、封装一个兼容性 getScrollOffset() 方法

Offset 是尺寸的意思,求滚动条滚动的尺寸,

这个方法一旦调用,就能实时的返回当前滚动条的滚动距离,返回的 x,  y 分别代表滚动条滚动的 x轴距离,y轴距离

function getScrollOffset(){
  if(window.pageXOffset){
    return {
      x : window.pageXOffset,
      y : window.pageYOffset
    }
  }else{
    return{
      x : document.body.scrollLeft + document.documentElement.scrollLeft,
      y : document.body.scrollTop + document.documentElement.scrollTop
    }
  }
}


// 再强调一下,写程序尽量符合Linuk写法
// function getScrollOffset(){
//   if(window.pageXOffset){
//     return { // 1. linuk写法就是,把大括号写到整个程序的后面(return {}后面)。
//       x: window.pageXOffset,
//       y: window.pageYOffset
//     }
//   }else{
//     return // 2. 不要这么写
//     {  
//       // 3. 其它情况也就罢了,但是在return的情况下,不按照linuk写法就return不回去对象了,return的是undefined空
//        // 4. 因为系统会自动在每一句后面,不写分号会自动给加分号(return ;)
//       x: document.body.scrollLeft + document.documentElement.scrollLeft,
//       y: document.body.scrollTop + document.documentElement.scrollTop
//     }
//   }
// }


测试用else下面的IE老方法

function getScrollOffset(){
  if(0 && window.pageXOffset){ // 让上面这个失效用下面的
    return {
      x: window.pageXOffset,
      y: window.pageYOffset
    }
  }else{
    return{
      x: document.body.scrollLeft + document.documentElement.scrollLeft,
      y: document.body.scrollTop + document.documentElement.scrollTop
    }
  }
}

返回的结果

image.png


把封装完的方法放到一个代码库里面tools.js,想用这个库里面的方法时引入这个库


二、查看视口的尺寸


什么是可视区窗口?

可视区窗口就是我们编写html文档能看到的部分

1、3WC标准模型下视口尺寸

求可视区窗口的尺寸分为很多个方法,首先W3C标准的方法,IE8及IE8以下不兼容


 window.innerWidth 

image.png


 window.innerHeight 


如果用 ctrl+ 放大页面,页面的尺寸也会跟着一起拉伸,求的时候页面尺寸会变小,就像比如用放大镜看,视觉上挺大其实很小


2、IE8以及IE8以下的方法是两个形式

标准模式下

document.documentElement.clientWidth

document.documentElement.clientHeight 


不标准模式也就是怪异模式下

document.body.clientWidth

document.body.clientHeight 


HTML文档以上第一行 <!DOCTYPE html> 启到很重要的作用,

标准模式:加上<!DOCTYPE html>就变成了标准模式

非标准模式:<!DOCTYPE html>删了浏览器的渲染模式就变成了怪异模式


标准模式和怪异模式是干什么的呢?

历史的问题,IE6到IE7版本的提升,肯定是摒弃了一些之前的东西,然后新启了一些新的东西,所以两个浏览器的语法规则包括所容纳的一些规范肯定不一样,


如果2001年IE6诞生开发的网站完全符合IE6语法,几年后后IE7诞生了,大多数用户采用IE7浏览之前IE6时候写的网页会有冲突,

IE6时候写的部分语法代码是根据IE6来写的,现在用IE7来访问网站势必存在一些不兼容的问题,这些问题唯一解决的办法就是重写。


后来大家研究可不可以有一种模式,即使是IE7访问给IE6写的代码,它依然可以按照IE6的语法规则来渲染页面,

后来大家研究出一种规则,让浏览器除了“标准的渲染模式”以外还要有一种渲染模式叫“怪异模式”,怪异模式唯一一点用途就是向后兼容,意思是比如IE7启动"怪异模式"它符合的语法规则不在是IE7是IE6,


每一个浏览器都有两种渲染模式,一种是渲染模式叫"标准模式",另一种是叫"怪异模式"也叫混杂模式,

怪异模式一经启动它所识别的语法就不在是最新的语法,而是试图去兼容之前的语法,

但是也不用太纠结这个问题,谷歌浏览器的现在的和之前的差别几乎看不到,唯一怪异模式有那么一点用处的就是IE的那几个问题,一般情况下是不需要的。


<!DOCTYPE html> 删了就是混杂模式,出来就是标准模式


<!DOCTYPE html>叫文档类型也叫DTD,DTD分为很多种形式具体的后面再讲,现在这是html5形式的DTD也是最简化的之前的要写很长


IE8及IE8以下浏览器访问视口

document.documentElement.clientWidth  在标准模式下IE8及IE8以下浏览器访问视口尺寸用的方法(client是计算机标准用语"客户端"的意思)

image.png


document.documentElement.clientHeight

image.png


在怪异模式下采用方法,怪异模式和标准模式就差一个documentElement

document.body.clientWidth

document.body.clientHeight


封装兼容性方法之前考虑一个问题,基本分为两种情况,

一种是IE9以上,

一种是IE9以下,

但是IE9以下是"标准模式"还是"怪异模式"怎么判断?

3、compatMode属性

有一个属性是document上的compatMode属性(compat是兼容性的意思) 


 document.compatMode  返回CSS1Compat,说明是标准模式

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>标准模式</title>
</head>
<body>
<script>

  console.log(document.compatMode); // CSS1Compat

</script>
</body>
</html>


把最上面的<!DOCTYPE html>删了,返回的是BackCompat

<!-- 注释掉<!DOCTYPE html> -->
<html>
<head>
<meta charset="UTF-8">
<title>怪异模式</title>
</head>
<body>
<script>

  console.log(document.compatMode); // BackCompat

</script>
</body>
</html>

BackCompat就是向后兼容的意思,通过document.compatMode返回的文字来区分是什么模式

4、封装兼容性函数

封装getViewportOffset()方法( Viewport是可视区窗口的意思 )

function getViewportOffset(){

  if(window.innerWidth){
    return {
      w: window.innerWidth,
      h: window.innerHeight
    }
  }else{
    if(document.compatMode === "BackCompat"){
      return {
        w: document.body.clientWidth,
        h: document.body.clientHeight
      }
    }else{
      return {
        w: document.documentElement.clientWidth,
        h: document.documentElement.clientHeight
      }
    }
  }
}

alert('w: ' + getViewportOffset().w + '  ' + 'h: ' + getViewportOffset().h);


强制用IE8及IE8以下的方法,会发现把滚动条占用的距离也计算进去了

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>视口尺寸</title>
<style>
div{width:10000px;height:10000px;}
</style>
</head>
<body>

<div style=""></div>

<script>

  function getViewportOffset(){
    if(0 && window.innerWidth){ // 强制走下面用IE8及IE8以下的方法
      return {
        w: window.innerWidth,
        h: window.innerHeight
      }
    }else{
      if(document.compatMode === "BackCompat"){
        return {
          w: document.body.clientWidth,
          h: document.body.clientHeight
        }
      }else{
        return {
          w: document.documentElement.clientWidth,
          h: document.documentElement.clientHeight
        }
      }
    }
  }

  alert('w: ' + getViewportOffset().w + '      ' + 'h: ' + getViewportOffset().h);

</script>
</body>
</html>


滚动条三种方法求出宽度,W3C的标准方法window.innerWidth求出的是整个屏幕分辨率宽度1920,IE8及IE8以下的方法有点区别

image.png


三、查看元素的几何尺寸


 getBoundingClientRect()  方法返回一个内容包,

内容包里面包含了这个元素所呈现宽高位置的一些信息,这个方法是任何DOM元素都可以调用的(Rect是方块的意思,square也是方块的意思,Rect是矩形square是正方形都是方块的意思)

<div style="width:100px;height:100px;background-color:orange;position:absolute;top:100px;left:100px;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];

  console.log(div.getBoundingClientRect()); // DOMRect {x: 100, y: 100, width: 100, height: 100, top: 100, …}

  /**
   * bottom: 200  bottom是下低,低边是200
   * height: 100  height属性IE里面没有
   * left: 100
   * right: 200
   * top: 100
   * width: 100   width属性IE里面没有
   * x: 100
   * y: 100
   */

</script>

div调用getBoundingClientRect()方法返回一个对象,这个对象包含了div元素的几乎一切信息

image.png


先看是一类

bottom:200

left:100

right:200

top:100

image.png


这四个方向是四个边的像素位置,换句话说求的是左上点右下点的坐标

image.png


然后还有两个信息一个是width一个是height,也就是说这个div的宽高位置都能求出来,

这也是getBoundingClientRect()的特点可以返回这样一个包,它的兼容性是比较好的,里面包含6个属性中width、height这两个求尺寸非常重要的属性在IE里没有


IE浏览器通过这个方法求宽高

 right - left = width  右侧边减左侧边求的是宽度

 bottom - top = heigth  底边减去top求的是高度

<div style="width:100px;height:100px;background-color:orange;position:absolute;top:100px;left:100px;"></div>

<script>

var div = document.getElementsByTagName('div')[0];
var box = div.getBoundingClientRect();

console.log("width: " + (box.right - box.left)); // width: 100
console.log("heigth: " + (box.bottom - box.top)); // heigth: 100

</script>

这个方法是es5的新方法,我们只作为了解就可以了,因为后期求元素尺寸的方法太多了,比这个方法好用的多的是


最后一点它返回的结果并不是实时的,它返回的是这个方块此时此刻静态的写照,什么是静态的?

<div style="width:100px;height:100px;background-color:orange;position:absolute;top:100px;left:100px;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];
  var box = div.getBoundingClientRect(); // 1. 变量box储存好这个结果,box是一个对象里面包含了很多数据,

  div.style.width = '200px'; // 2. div元素的宽改成200像素
  console.log(box); // 3.DOMRect {x: 100, y: 100, width: 100, height: 100, top: 100, …}

  /*
  现在依然访问box元素的值和刚才是一模一样的,width还是100、right还是100尺寸位置都没有变
  bottom: 200
  height: 100 
  left: 100
  right: 200   right还是100
  top: 100
  width: 100   width还是100
  x: 100
  y: 100
  __proto__: DOMRect
  */

  console.log(div.getBoundingClientRect()); // 重新获取一次width变成200,相应的左边距right变成300
  /*
  bottom: 200
  height: 100
  left: 100
  right: 300
  top: 100
  width: 200
  x: 100
  y: 100
  __proto__: DOMRect
  */

</script>


我们了解js里面提供了一个这样的方法就行了,只不过js里面提供了很多方法,方法和方法之间难免会有冲突,难免有一些方法会让另一些方法成为永久的不可能开启的方法了


四、查看元素的尺寸


dom.offsetWidth

dom.offsetHeight

这些方法就是让的 getBoundingClientRect() 成为永远报废的方法,这两个方法也是查看一个元素尺寸的,只不过这回查看元素尺寸方便多了


任何一个DOM元素都可以调用 dom.offsetWidth/offsetHeight,分别宽高返回100

<div style="width:100px;height:100px;background-color:aqua;position:absolute;top:100px;left:100px;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];

  console.log(div.offsetWidth);  // 100

  console.log(div.offsetHeight); // 100

</script>


探究一下,

求出的width和heigth是实际内容区的宽高还是看起来视觉上的宽高?


div元素加100像素padding,现在再求div的宽高返回的是300像素

<div style="padding:100px;width:100px;height:100px;background-color:aqua;position:absolute;top:100px;left:100px;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];

  console.log(div.offsetWidth); // 300

  console.log(div.offsetHeight); // 300
    
</script>


就想求div元素内容区的宽高怎么求?用刚才的 getBoundingClientRect() 方法返回的也是300

<div style="padding:100px;width:100px;height:100px;background-color:aqua;position:absolute;top:100px;left:100px;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];
    
  console.log(div.getBoundingClientRect()); // DOMRect {x: 100, y: 100, width: 300, height: 300, top: 100, …}

    
</script>


这两个方法返回的都是div看起来的尺寸,既然两个方法的功能都一样,那offsetWidth就完全把getBoundingClientRect()给取代了


求div内容区宽高,需要后续的知识  div.style.height  这是间接的来访问CSS

<div style="padding:100px;width:100px;height:100px;background-color:aqua;position:absolute;top:100px;left:100px;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];

  console.log(div.style.height); // 100px

  console.log(div.style.width); // 100px
    
</script>


dom.offsetWidth

dom.offsetHeight

这两方法求的是元素视觉上的尺寸包含width、height、padding、border不包含margin


五、查看元素的位置


1、查看元素的位置有两个方法  dom.offsetLeft  和  dom.offsetTop 

div的位置left和top值都返回了100,但是这两个方法没有想的那么理想

<div style="padding:100px;width:100px;height:100px;background-color:aqua;position:absolute;top:100px;left:100px;"></div>

<script>

  var div = document.getElementsByTagName('div')[0];

  console.log(div.offsetLeft); // 100

  console.log(div.offsetTop); // 100

</script>


现在把这个div放到另外一个大div里面

<div style="width:300px; height:300px; border:1px solid saddlebrown; position:relative;top:100px;left:100px;">
  <div class="demo" style="width:100px;height:100px;background-color:aqua;position:absolute;top:100px;left:100px;"></div>
</div>

为什么里面的div会在外边的大div中间呢?

因为里面div设置的是 position:absolute、left:100、top:100,而绝对定位position:absolute是相对于它有定位的父级定位的,正好外面的大div有position:relative定位,所以里面的div在中间

image.png


下面求里面的小div在页面里的位置,其实它在页面里的位置应该是left:200、top:200,但是里面的小 div.offsetLeft 返回的是100像素,它求的不是相对于页面那个的位置,求是相对于父级的位置

<div style="width:300px; height:300px; border:1px solid saddlebrown; position:relative;top:100px;left:100px;">
  <div class="demo" style="width:100px;height:100px;background-color:aqua;position:absolute;top:100px;left:100px;"></div>
</div> 

<script>

  var div = document.getElementsByClassName('demo')[0]; // 用class选择器选择里面的div(里面的div加一个class="demo")

  console.log(div.offsetLeft); // 100
    
</script>


这块理解是,里面的div首先有定位,然后在相对应它父级的位置才是我们求的值,如果里面的小div没有定位呢?


如果里面的小div居中不是通过改变top、left,而是通过改变margin的这个方法还好用吗?下面返回的还是100

<div style="width:300px;height:300px;border:1px solid saddlebrown;position:relative;top:100px;left:100px;">
  <div class="demo" style="width:100px;height:100px;background-color:aqua;position:absolute;margin-left:100px; margin-top:100px;"></div>
</div>

<script>

  var div = document.getElementsByClassName('demo')[0];

  console.log(div.offsetLeft); // 返回的还是100
    
</script>


解释一下,

这个 offsetLeft 真正的含义是忽略自身是不是定位元素,它不管自身是不是定位元素,它求的只是这个元素距它有定位的父级的距离


不管这个距离是怎么生成的,是margin生成的还是定位生成的它都不管,但凡里面的小div和有定位的父级有距离就把这个距离的值反回来,

offsteLeft这个left指的不是定位的那个left,它求的就是横向上我和你的距离的left,表示的是一个方向不是语法,

但有一点,里面的小div关注的是外面的div是不是有定位的父级,如果是有定位的父级,那里面小div求的offsetLeft就是距离这个有定位父级的一个距离,如果外面的div不是有定位的父级,那求的就是距离外层边框。


总结:

对于无定位父级的元素,返回相对文档的坐标。

对于有定位父级的元素,返回相对于最近的有定位的父级的坐标(无论是left还是margin-left等都算距离)


试一下,把外面的大div父级也变成不是定位的,position的默认值是static(static是静态的)

<div style="width:300px;height:300px;border:1px solid saddlebrown;position:static;margin-left:100px;margin-top:100px;">
    <div class="demo" style="width:100px;height:100px;background-color:aqua;position:absolute;margin-left:100px;margin-top:100px;"></div>
</div>

<script>

  var div = document.getElementsByClassName('demo')[0];

  console.log(div.offsetLeft); // 209 = 边框1px + body里面默认的margin:8px

  console.log(div.offsetTop); // 201 = 边框1px + 父元素大div的margin-top:100px与body默认纵向的margin:8px它两重叠塌陷掉了

  // PS: 横向的两个margin会相加,纵向的margin肯定会出塌陷的问题

</script>


2、dom.offsetParent

还有一个方法  dom.offsetParent  能把最近的有定位的父级求出来,现在里面的小div最近没有有定位的父级

<div style="width:300px;height:300px;border:1px solid saddlebrown;margin-left:100px;margin-top:100px;">
  <div class="demo" style="width:100px;height:100px;background-color:aqua;position:absolute;margin-left:100px;margin-top:100px;"></div>
</div>

<script>

  var div = document.getElementsByClassName('demo')[0];

  console.log(div.offsetParent); // 没有有定位的父级返回<body>...</body>

  console.log(div.offsetParent.offsetParent); // 最顶级就是body了,body.offsetParent没有返回null

</script>


如果外面的大DIV有定位加上 position:relative 定位

<div style="width:300px;height:300px;border:1px solid saddlebrown;margin-left:100px;margin-top:100px;position:relative;">
    <div class="demo" style="width:100px;height:100px;background-color:aqua;position:absolute;margin-left:100px;margin-top:100px;"></div>
</div>

<script>

  var div = document.getElementsByClassName('demo')[0];

  console.log(div.offsetParent); // 返回的是外边的大div

</script>

返回的是外边的大div

image.png

3、作业

求任意一个元素相对于文档的坐标,给一个元素不知道它有没有定位的父级,还不知道有多少位定位的父级。

思路是什么?

1 先看它有没有有定位的父级,如果有先求它距离有定位父级的距离。

2 然后在把视角换在它有定位的父级上,看看它父级上面还有没有有定位的父级。

3 一段一段的加一直加到最后,这是一个循环递归的过程

函数名叫getElementPosition()


六、让滚动条滚动


之前是获取信息,

1. 滚动条滚动的距离

2. 窗口尺寸

3. 元素尺寸/距离


让滚动条滚动,这节是操作,

window上有三个方法scroll()、scrollTo()、scrollBy()


其实这三个方法就两个方法,前边这两个方法scroll() 和 scrollTo() 实现的是一样的用那个都行


1、scroll() 和 scrollTo() 两个方法是一样的

 window.scroll(x, y)  能让滚动条滚动,里面填写两个参数,

参数一,让x滚动轴滚动距离

参数二,让y轴滚动条滚动距离


window.scroll(0 ,100) 第一个x轴没有填0,第二个填100,执行就会走到滚动条滚动到100的位置

image.png


再执行一次不动,再执行一次还不动…说明什么?

image.png

说明让滚动条滚动到当前位置,而不是累计滚动到当前距离。


window.scroll 和 window.scrollTo 没有任何差别是一样的,

window.scrollTo(0 ,100) 执行滚动到100的位置,反复执行也是一样的,兼容性都是一样的,不知道为什么出两个方法!

image.png


2、scrollBy()

第三个方法 scrollBy() 和上面两个方法不一样, window.scrollBy(0, 10) 第一个参数也是x轴距离,第二个参数是y轴距离,但是scrollBy是累加滚动距离


 window.scrollBy(0, 10)  执行会向下滚动10个像素

image.png


再执行再执行再执行…就一直会往下累加

image.png

scrollBy不是滚动多少个点,是还要往下滚动多少个距离。


写负-10滚动上去了 window.scrollBy(0, -10); 

image.png

通过动态改变滚动条,这样累加滚动可以调整页面的滚动


3、作业

利用scrollBy()快速阅读的功能,点击start滚动,点击stop停止滚动

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>小例子,自动阅读</title>
<style>
body{margin:0; height:10000px;}
</style>
</head>
<body>

<div style="height:60px;width:60px;position:fixed;bottom:130px;right:30px;background:#eee;text-align:center;line-height:60px;cursor:pointer;opacity:0.9;">Stop</div>
<div style="height:60px;width:60px;position:fixed;bottom:200px;right:30px;background:#eee;text-align:center;line-height:60px;cursor:pointer;opacity:0.9;">Start</div>

<script>

  var stop =  document.getElementsByTagName('div')[0],
    start = document.getElementsByTagName('div')[1],
    timer = 0;
  
  start.onclick = function(){ // 点击开始自动阅读
    timer = setInterval(function(){
      window.scrollBy(0, 10);
    },100);
  }
  
  stop.onclick = function(){ // 点击停止定时器
    clearInterval(timer);
  }

</script>
</body>
</html>

但是有一个问题,多次点击start会加速,因为产生了多个定时器,点击stop停不了


解决方法加一个锁

var stop =  document.getElementsByTagName('div')[0],
    start = document.getElementsByTagName('div')[1],
    timer = 0,
    key = true; // 加一个开关,一开始是true

start.onclick = function(){
  if(key){
    timer = setInterval(function(){
      window.scrollBy(0, 10);
      // window.scrollBy(0, num); 加速调节num,单独设置一个按键给num加
    },100);

    key = false; // 1. 开启定时间后把锁关上
    console.log(key);
  }
}

stop.onclick = function(){
  clearInterval(timer);
  key = true; // 点击stop时把锁在打开
  console.log(key);
}



Leave a comment 0 Comments.

Leave a Reply

换一张