手把手带你用原生js实现css属性的set和get

xiaojy822 8年前
   <p>我们就来实现一个简易版的小插件,能够在不借助jQuery的情况下实现css属性的获取和设置。</p>    <h2>Let’s start</h2>    <p>首先创建一个 css-tool.js 文件,一开始他是这个样子的:</p>    <pre>  ;(function(window,undefined){   "use strict";      var cssTool = function(){      return new cssTool.prototype.init();    }      cssTool.prototype = {      init: function(){        console.log('init success');        return this;      },    }      cssTool.prototype.init.prototype = cssTool.prototype;    // 暴露接口    window.cssTool = cssTool;    })(window);  </pre>    <p>全局作用域可以看作是一栋公寓楼,我们创建一个立即执行的匿名函数,相当于是公寓楼中的一间公寓,我们在屋子里做的事情就都是隐秘的,也就是起到隔离作用域的作用,避免和外部变量产生冲突。把 window 作为参数拿到屋子里来,以后就不用再重复去外面找 window 来用。最前面的分号是为了保证在文件合并压缩后产生语法错误。 undefined 在老版本浏览器中不被支持,因此考虑到兼容性添加一个形参。</p>    <p>我们创建了一个叫 cssTool 的私有方法,相当于我们在屋子里找了一个小房间来放 get 和 set 等方法。接下来我们在原型上新增一个 init 方法,用于初始化。之后我们仿照 jQuery 的方式,将 init 的 prototype 指向 cssTool 的 prototype ,这样我们在用 init 作为构造函数创造实例时,可以使插件拥有两种调用方式:</p>    <ul>     <li>var ct = new cssTool() 构建 cssTool 实例</li>     <li>直接调用 cssTool() ,一样返回 cssTool 实例</li>    </ul>    <h2>get方法</h2>    <h2>通用方式</h2>    <h3>现代浏览器和IE9+</h3>    <pre>  window.getComputedStyle(elem,null).getPropertyValue(attr)  </pre>    <h3>IE678</h3>    <pre>  elem.currentStyle.getAttribute(camelCase(attr))  </pre>    <h2>兼容处理</h2>    <h3>驼峰命名法转换-camelCase</h3>    <p>对 currentStyle 来说,在IE6浏览器中他很专一,只喜欢以驼峰命名法命名的变量,而IE78中就有点朝三暮四,驼峰命名法和中间带’-‘的都照单全收,为了兼容和操作的简便,我们统一转换为驼峰命名法。</p>    <pre>  /**   * 驼峰命名法转换,IE678使用   * font-size --> fontSize   * @param {String} attr   * @param {String} match  匹配到的字符串,如-c   * @param {String} originText (\w)是一个捕获,这里是捕获到的字符,如c   * @return 返回驼峰命名方式的css属性名   */  functioncamelCase(attr){    return attr.replace(/\-(\w)/g, function(match,originText){      return originText.toUpperCase();    });  }  </pre>    <h3>透明度获取-getFilter</h3>    <p>IE678的透明度是通过 filter:alpha(opacity=0) 来设置的,我们利用正则表达式匹配到此时透明度的值,由于此时得到的是0-100之间的数值,所以需要换算为我们常见的0-1的形式。</p>    <pre>  /**   * IE678下获取透明度的值   * @param  elem 获取值的 dom   * @return {Number} 透明度的值,默认为1   * IE678下设置透明度 filter: alpha(opacity=0) 取值为0-100   */  functiongetFilter(elem){    var _filter = elem.currentStyle.getAttribute('filter').match(/alpha\(opacity=(.*)\)/i);    var value = parseFloat(_filter[1]);    if(!isNaN(value)){      // 转化为0-1      return value ? value/100 : 0;    }    return 1;  }  </pre>    <h3>float 值的获取</h3>    <p>上一篇博客中提到,由于 float 是 ECMAScript 的一个保留字。所以在各浏览器中都会有代替的写法,比如说在现代浏览器中为 cssFloat ,而在 IE678 中为 styleFloat 。经测试,在现代浏览器中直接使用 getPropertyValue("float") 也可以获取到 float 的值。而 IE678 则不行,所以针对 float ,需要简单的hack。</p>    <h3>width | height 样式的获取</h3>    <p>对于一个没有设定高宽的元素而言,在 IE678 下直接获取得到的值是 auto 。而现代浏览器会直接返回它的 px 值,我们的目标就是在 IE 下也返回 px 值。</p>    <pre>  // 直接获取外部样式表未设置的 width 和 height 返回值为 auto  // 为了获取精确的 px 值,使用 getBoundingClientRect 方法  // getBoundingClientRect 可以获得元素四个点相对于文档视图左上角  // 的 top、left、bottom、right值,进行简单计算即可  var condition = attr === 'width'               || attr === 'height'               && elem.currentStyle[attr] === 'auto';  if(condition){    var clientRect = elem.getBoundingClientRect();    return (attr === 'width' ?            clientRect.right - clientRect.left :            clientRect.bottom - clientRect.top           ) + 'px';  }  </pre>    <h2>set方法</h2>    <p>set 方法相较于 get 方法要简便的多,因为我们有 cssText 这个跨越 IE6+ 和现代浏览器的神器。</p>    <p>通过 elem.style.cssText 可以对元素的样式进行读写,实际上操作的是 html 标签上的 style 属性的值。因此不能直接对其赋值,不然就把整个 style 属性的值给覆盖掉了。我们采用累加的方式来修改属性。</p>    <p>另外,在IE浏览器还有个小坑,如果 cssText 不为空,返回值最后一个分号会被删掉,因此我们需要在累加的属性前加上一个分号。</p>    <pre>  /**   * 设置元素css样式值   * @param elem 设置值的dom元素   * @param {String} attr 设置样式名称,如font-size   * @param {String} value 设置样式的值,如16px   */  set: function(elem, attr, value){    // IE78 设置透明度需特殊处理    if(attr === 'opacity'){      // 针对 IE7 的 hack      // filter 滤镜要求 hasLFooout=true 才能执行      // IE浏览器且 hasLFooout=false 时执行      if(!!elem.currentStyle && !elem.currentStyle.hasLFooout){        elem.style.zoom = 1;        attr = 'filter';        value = 'alpha(opacity=' + value * 100 + ')';      }    }    // 通用方式    elem.style.cssText += ';' + (attr + ':' + value) + ';';  }  </pre>    <h2>补充</h2>    <h2>简单解释new操作符的作用</h2>    <pre>  var Foo = function(){    return new Foo.prototype.init();  }    Foo.prototype = {    init: function(){      this.age = 18;      return this;    },    age: 20  }    console.log(Foo().age); // 18  console.log(Foo.prototype.age); // 20  </pre>    <pre>  var Foo = function(){    return Foo.prototype.init();  }    Foo.prototype = {    init: function(){      this.age = 18;      return this;    },    age: 20  }    console.log(Foo().age); // 18  console.log(Foo.prototype.age); // 18  </pre>    <p>使用 new 操作符时,是把 init 当成构造函数来调用,在 init 内部会创建一个隐式对象并用 this 指向它,此时的 this.age=18 表示在这个隐式对象上增加一个 age 属性,最后 return this 不是必需的,构造函数默认会返回 this 。此时 Foo.prototype.age 不受影响。</p>    <p>当不使用 new 操作符时,相当于一个​普通对象上的函数调用, this 指向了 init 所属的对象,即 Foo.prototype , this.age=18 相当于对 Foo.prototype.age 赋值,和使用 new 操作符是有本质区别的。</p>    <h2>小结</h2>    <p>到这里,教程也就要告一段落了。一个 jQuery 中常见的 css() 方法背后涵盖了非常多的知识点,跨浏览器的兼容性也是我们此次讨论的重点,这次只是实现了一个非常简易的 css 操作插件。学问尚浅,如果有不清楚或者有错误的地方,欢迎各位留言或者提issue来帮助我改进这个小插件。</p>    <p> </p>    <p> </p>    <p>来自:http://xdlrt.github.io/2017/02/05/2017-02-05/</p>    <p> </p>