1.JS盒子模型 #

通过JS中提供的一系列属性和方法,获取页面中元素样式信息

<div id="box" name="abc" class="container" index="1"></div>
box.attributes.getNamedItem("id");
//通过属性名获取属性值
box.classList.add('test');
box.classList.remove('container');
//通过classList操作class属性值
//将div区域设置为可编辑
box.contentEditable = true

1.1 内容的宽度/高度 #

1.2 内容的偏移 #

若左右/上下边框一样,则offsetWidth = clientWidth+clientLeft*2

1.3 内容的滚动 #

如果容器有内容溢出,则:scrollWidth为真实内容宽度加上左填充,scrollHeight为真实内容高度加上上填充。
同一个浏览器,对于元素是否设置overflow:hidden样式而获取的到的结果是不一样的;在不同的浏览器中获取到的结果也是不一样的,获取到的结果都是约等于值

关于JS盒子模型的取值问题 通过这些属性值获取的结果永远都是整数,浏览器在获取结果时会在原值基础上自动进行四舍五入

1.4 关于操作浏览器本身盒子模型 #

clientWidth/clientHeight获取当前浏览器可视窗口宽高
document.documentElement.scrollHeight当前整个页面的实际高度,但是是一个约等于值;
document.documentElement.scrollTop值一直为0,若要获取卷去的高度,则应该使用

document.body.scrollTop

兼容写法,准备两套获取方法:

document.documentElement[attr] || document.body[attr];

若设置值也要写两套 编写一个浏览器盒子模型的方法

//若只传了attr则默认为获取属性值;若都传递了两个参数,则为设置该属性值
function win(attr,val){
    //获取
    if(typeof val === "undefined"){
        return document.documentElement[attr] || document.body[attr];
    }
    //设置
    document.documentElement[attr] = val;
    document.body[attr] = val;
}

1.5 获取元素具体样式值 #

  1. ele.style.width 这种方式只能获取元素的行内样式,css中的获取不到

  2. window提供了getComputedStyle()方法,获取所有经过浏览器计算过的样式

  3. 浏览器计算过的样式:只要当前元素标签在页面中显示,就表示它的所有样式经浏览器计算过或渲染过

    window.getComputedStyle(curEle,null);
    //获取的是元素所有的样式内容
    

    但是在IE6-8是不兼容的,因为window下没有这个属性;在IE6-8可以使用currentStyle来获取所有经浏览器计算过的样式(currentStyle仅在IE6-8下存在)

    var a = box.currentStyle.width;
    
  4. 处理兼容的方式:

    function getCss(curEle,attr){
    //获取当前元素所有经过浏览器计算过的样式
    var val = null;
    try{
     val = window.getComputedStyle(curEle,null)[attr];
    }catch(e){
     val = curEle.currentStyle[width];
    }
    return val;
    }
    

    这个方法必须保证try中的代码在不兼容的浏览器中执行的时候报错,这样才能用catch捕获并进行处理;所以不管当前浏览器是否兼容该方法,都要先执行一遍try块中的代码(这样比较消耗性能),try/catch只有在万不得已的时候才拿来处理

  5. 判断当前浏览器是否存才属性或方法
    function getCss(curEle,attr){
     var val = null;
     if("getComputedStyle" in window){
     //if(window.getComputedStyle)
     //若返回true表示window下有该属性
         val = window.getComputedStyle(curEle,null)[attr];
     }else{
         val = curEle.currentStyle[attr];
     }
     retur val;
    }
    

1.6 检测浏览器版本和类型 #

window.navigator.userAgent
function getBrowserInfo(){
    var agent = navigator.userAgent.toLowerCase() ;

    var regStr_ie = /msie [\d.]+;/gi ;
    var regStr_ff = /firefox\/[\d.]+/gi
    var regStr_chrome = /chrome\/[\d.]+/gi ;
    var regStr_saf = /safari\/[\d.]+/gi ;
    //IE
    if(agent.indexOf("msie") > 0){
        return agent.match(regStr_ie) ;
    }

    //firefox
    if(agent.indexOf("firefox") > 0){
        return agent.match(regStr_ff) ;
    }

    //Chrome
    if(agent.indexOf("chrome") > 0){
        return agent.match(regStr_chrome) ;
    }

    //Safari
    if(agent.indexOf("safari") > 0 && agent.indexOf("chrome") < 0){
        return agent.match(regStr_saf) ;
    }
}

var browser = getBrowserInfo() ;
//alert(browser); 
var verinfo = (browser+"").replace(/[^0-9.]/ig,"");

判定当前浏览器为IE6-8

if(/MSIE (6|7|8)/.test(navigator.userAgent)){...}

1.7 升级getCss() #

1.7.1 去掉单位 #

getComputedStyle和currentStyle获取到的样式值都是带单位的,如:“px”;而clientWidth获取到的是不带单位的。有必要去掉单位以便于计算。

var reg = null;
reg = /^(-?\d+(\.\d+)?)(px|pt|rem|em)?$/i;
return reg.test(val) ? parseFloat(val) : val;

1.7.1 复合样式 #

getCss(ele,"border");
getCss(ele,"fontFamily");

在IE和谷歌浏览器里获取的值还是不一样的,主要是因为getComputedStyle和currentStyle在某些方面还是有区别的;这之间的差别没有办法可以解决,只能注意复合样式获取时的差异;另外,在IE和标准浏览器之间有个属性opacity也存在不同的处理方式

function getCss(curEle,attr){
    var val = null;
    var reg = null;
    if("getComputedStyle" in window){
        //if(window.getComputedStyle)
        //若返回true表示window下有该属性
        val = window.getComputedStyle(curEle,null)[attr];
    }else{
        //IE 情况范围
        if(attr === "opacity"){
          val = curEle.currentStyle["filter"];
          reg = /^alpha\(opacity=(\d+?:\.\d+)?\)$/i;
          val = reg.test(val) ? reg.exec(val)[1] / 100 : 1;
        }else{
          val = curEle.currentStyle[attr];
        }
        val = curEle.currentStyle[attr];
    }
    reg = /^(-?\d+(\.\d+)?)(px|pt|rem|em)?$/i;
    return reg.test(val) ? parseFloat(val) : val;
}

1.8 CSS伪类 #

window.getComputedStyle(curEle,null) 获取伪类元素的样式和内容

#para:before{
  display: block;
  width: 100px;
  height: 30px;
  content: "Hello World!";
}
<p id="para">你好</p>
var con = window.getComputedStyle(para,"before").content;
var wth = window.getComputedStyle(para,"before").width;
var dis = window.getComputedStyle(para,"before").display;

1.9 获取元素偏移量 #

offsetParent:父级参照物,在同一个平面中最外层的元素是所有元素的父级参照物,和层级没有必然联系

一般来说页面中所有元素的父级参照物都是body

body是没有参照物的document.body.offsetParent == null

想要改变父级参照物需要position定位来实现

<div id="outer">
    <div id="inner">
        <div id="center">

        </div>
    </div>
</div>
outer.style.position = "relative/absilute/fixed";
console.log(inner.offsetParnet) //div.outer
console.log(center.offsetParnet) //div.outer
console.log(outer.offsetParnet) //body

若将inner也进行设置

inner.style.position = "relative";
console.log(inner.offsetParnet) //div.outer
console.log(center.offsetParnet) //div.inner
console.log(outer.offsetParnet) //body

offsetLeft/offsetTop当前元素的外边框距离当前父级参照物的内边框的距离

在标准的IE8浏览器中,使用offsetLeft/offsetTop时其实已经把父级参照物的边框已经计算在内了

function offset(curEle){
    var totalLeft = null;
    var totalTop = null;
    var parent = curEle.parentNode;
    while(parent){
        //累加父级参照物的左偏移
        totalLeft += curEle.offsetLeft;
        //累加父级参照物的边框
        if(navigator.userAgent.indexOf("MSIE 8.0") === -1){
            //非标准IE8浏览器
            totalLeft += curEle.clientLeft;
            parent = parent.parentNode;
        }

    }
    return totalLeft;
}

1.10 回到顶部 #

1.10.1 锚点 #

<p id="top"></p>
<a href="#top">TOP</a>

1.10.2 scrollTop #

scrollTop/scrollLeft存在边界值(最大值和最小值),如果设置的值超过了边界值也不会起作用

最大值 = 容器真实高度 - 当前一屏的高度

var maxTop = box.scrollTop - box.clientHeight;
aa.onclick = function(){
    var scrollTop = document.body.scrollTop;
    var screenHeight = document.documentElement.clientHeight;
    var duration = 500;
    var interval = 10;
    var step = (scrollTop/duration)*interval;
    var timer = window.setInterval(function(){
        var cur = document.body.scrollTop;
        if(cur <= 0){
            clearInterval(timer);
        }
        cur -= step;
        document.body.scrollTop = cur;
    },interval);
}

2. JS的异步编程 #

JS为单线程,在当前事情没有完成时是不会做下一件事情

异步的四种情况:
1.定时器
2.事件绑定
3.ajax读取数据
4.回调函数

每一个浏览器对于定时器的等待时间都有一个最小等待时间;即使设置等待时间小于最小等待时间,也不起作用(谷歌等待时间约为5-6ms,IE等待时间约为10-13ms)

var n = 0;
setTimeout(function(){
  n++;
  console.log(n);
},0);
console.log(n);

定时器的等待时间不一定就是最后执行时间,如果定时器后还有其他事情要处理,不管定时器的时间有没有到达都不会执行

var n = 0;
setTimeout(function(){
  n++;
  console.log(n);
},1000);
console.log(n);
while(1){  //死循环
  n++;
}
console.log(n)

结果只输出一次,为“0”

2.1 图片延迟加载 #

作用:保证页面打开的速度
原理:首屏内容图片,先用默认图片占位(一般在5kb以内),当首屏内容加载完成后再加载首屏的图片;其他屏幕的图片,也是一张默认图片占位,当滚动条滚到对应区域后再加载图片

网站性能优化:尽量减少http请求

html,css,img,js文件都会向服务器发起请求,优化方法:

2.2 单张图片超过一屏后延迟加载 #

图片距离顶部的距离:图片距body的上偏移+图片高度
浏览器底部距离顶部:浏览器一屏高度+页面卷去的高度
懒加载 当图片从下往上露出时开始延迟加载,即图片底部距离页面顶部的距离小于浏览器底部距离顶部的距离

var ban = document.querySelector('.banner');
var img = ban.getElementsByTagName('img')[0];
window.onscroll = function(){
    if(ban.loaded){
        return
    }
    var a = ban.offsetHeight + ban.offsetTop;
    var b = document.documentElement.clientHeight + document.body.scrollTop;
    console.log(a,b);
    if(a<b){
        var oImg = new Image;
        oImg.src = img.getAttribute("trueSrc");
        oImg.onload = function(){
            img.src = this.src;
            img.style.display = "block";
            oImg = null;
        }
        ban.loaded = true;
    }
}

3.DOM方法 #

3.1 children #

获取某一容器的元素子节点,只考虑子元素而不考虑所有后代元素 ele.children只能获取儿子级的子元素,而且不兼容

//获取ele的子级元素
function children(ele,tagname){
  var arr = [];
  if(/MSIE (6|7|8)/i.test(navigator.userAgent)){
    //IE8及以下
    var nodeList = ele.childNodes;
      for(var i=0;i<nodeList.length;i++){
        var cur = nodeList[i];
        if(cur.nodeType === 1){
          arr[arr.length] = cur;
        }
      }
  }else{
    //标准浏览器
    arr = Array.prototype.slice.call(ele.children);
  } 
  //二次筛选指定标签
  if(typeof tagname === "string"){
    for(var i=0;i<arr.length;i++){
      var cur = arr[i];
      if(cur.tagName.toLowerCase() !== tagname.toLowerCase()){
        arr.splice(i,1);
        i--;
      }
    }
  }
  return arr;
}

3.2 append,prepend,insertBefore,insertAfter #

//在指定容器的末尾追加元素
function append(newEle,container){
  container.appendChild(newEle);
}
//获取指定元素的第一个子元素
function first(ele){
  var arr = children(ele)
  return arr.length>0?arr[0]:null;
}
//在指定容器的开头追加元素
function prepend(newEle,container){
  var first = window.first(container);
  if(first){
    container.insertBefore(newEle,first);
    return;
  }
  //如果第一个元素都不在,表明该容器没有子元素
  container.appendChild(newEle);
}
//在指定元素之前追加元素
function insertBefore(newEle,oldEle){
  oldEle.parentNode.insertBefore(newEle);
}
//获取指定元素的后一个元素
function next(ele){
  if("getComputedStyle" in window){
    return ele.nextElementSibling;
  }
  var next = ele.nextSibling;
  while(next && next.nodeType !== 1){
    next = next.nextSibling;
  }
  return next;
}
//在指定元素之后追加元素
function insertAfter(newEle,oldEle){
  var next = window.next(oldEle);
  if(next){
    oldEle.parentNode.insertBefore(newEle,next);
    return ;
  }
  //如果下一个元素都不在,表明该该元素已为最后一个元素
  oldEle.parentNode.appendChild(newEle);
}

3.3 操作元素样式类名 #

function hasClass(ele,className){
    var reg = new RegExp("(^| +)"+className+"( +|$)");
    return reg.test(ele.className);
}
function addClass(ele,className){
    //防止className中包含多个类名
    var ary = className.split(/ +/g);
    for(var i=0;i<arr.length;i++){
        var cur = arr[i];
        if(!this.hasClass(ele,cur)){
            ele.className += " " + cur;
        }
    }
}
function removeClass(ele,className){
    var ary = className.split(/ +/g);
    for(var i=0;i<arr.length;i++){
        var cur = arr[i];
        if(this.hasClass(ele,cur)){
            var reg = new RegExp("(^| +)"+cur+"( +|$)","g");
            ele.className = ele.className.replace(reg," ");
        }
    }
}

JS异步编程思想 #