您的位置:首页 > Web前端 > CSS

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

2017-11-02 13:52 1076 查看
https://segmentfault.com/a/1190000008258365


Let's start

首先创建一个 
css-tool.js
 文件,一开始他是这个样子的:
;(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);


全局作用域可以看作是一栋公寓楼,我们创建一个立即执行的匿名函数,相当于是公寓楼中的一间公寓,我们在屋子里做的事情就都是隐秘的,也就是起到隔离作用域的作用,避免和外部变量产生冲突。把 
window
 作为参数拿到屋子里来,以后就不用再重复去外面找 
window
 来用。最前面的分号是为了保证在文件合并压缩后产生语法错误。 
undefined
 在老版本浏览器中不被支持,因此考虑到兼容性添加一个形参。

我们创建了一个叫 
cssTool
 的私有方法,相当于我们在屋子里找了一个小房间来放 
get
 和 
set
 等方法。接下来我们在原型上新增一个 
init
 方法,用于初始化。之后我们仿照 
jQuery
 的方式,将
init
的 
prototype
 指向 
cssTool
 的 
prototype
 ,这样我们在用
init
作为构造函数创造实例时,可以使插件拥有两种调用方式:

var ct = new cssTool()
构建 
cssTool
 实例

直接调用
cssTool()
,一样返回 
cssTool
 实例


get方法


通用方式


现代浏览器和IE9+

window.getComputedStyle(elem,null).getPropertyValue(attr)


IE678

elem.currentStyle.getAttribute(camelCase(attr))


兼容处理


驼峰命名法转换-camelCase

对 
currentStyle
 来说,在IE6浏览器中他很专一,只喜欢以驼峰命名法命名的变量,而IE78中就有点朝三暮四,驼峰命名法和中间带'-'的都照单全收,为了兼容和操作的简便,我们统一转换为驼峰命名法。
/**
* 驼峰命名法转换,IE678使用
* font-size --> fontSize
* @param {String} attr
* @param {String} match  匹配到的字符串,如-c
* @param {String} originText (\w)是一个捕获,这里是捕获到的字符,如c
* @return 返回驼峰命名方式的css属性名
*/
function camelCase (attr){
return attr.replace(/\-(\w)/g, function (match,originText) {
return originText.toUpperCase();
});
}


透明度获取-getFilter

IE678的透明度是通过 
filter:alpha(opacity=0)
 来设置的,我们利用正则表达式匹配到此时透明度的值,由于此时得到的是0-100之间的数值,所以需要换算为我们常见的0-1的形式。
/**
* IE678下获取透明度的值
* @param  elem 获取值的 dom
* @return {Number} 透明度的值,默认为1
* IE678下设置透明度 filter: alpha(opacity=0) 取值为0-100
*/
function getFilter(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;
}


float 值的获取

上一篇博客中提到,由于 
float
 是 ECMAScript 的一个保留字。所以在各浏览器中都会有代替的写法,比如说在现代浏览器中为
cssFloat
,而在
IE678 中为 
styleFloat
 。经测试,在现代浏览器中直接使用 
getPropertyValue("float")
 也可以获取到 
float
的值。而
IE678 则不行,所以针对 
float
 ,需要简单的hack。


width | height 样式的获取

对于一个没有设定高宽的元素而言,在 IE678 下直接获取得到的值是 
auto
 。而现代浏览器会直接返回它的 px 值,我们的目标就是在
IE 下也返回 px 值。
// 直接获取外部样式表未设置的 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';
}


set方法

set 方法相较于 get 方法要简便的多,因为我们有 
cssText
 这个跨越 IE6+ 和现代浏览器的神器。

通过
elem.style.cssText
可以对元素的样式进行读写,实际上操作的是 html 标签上的 
style
 属性的值。因此不能直接对其赋值,不然就把整个 
style
 属性的值给覆盖掉了。我们采用累加的方式来修改属性。

另外,在IE浏览器还有个小坑,如果 cssText 不为空,返回值最后一个分号会被删掉,因此我们需要在累加的属性前加上一个分号。
/**
* 设置元素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) + ';';
}


补充


简单解释new操作符的作用

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

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


使用 new 操作符时,是把 
init
 当成构造函数来调用,在 
init
 内部会创建一个隐式对象并用 
this
 指向它,此时的 
this.age=18
表示在这个隐式对象上增加一个
age 属性,最后 
return this
 不是必需的,构造函数默认会返回 
this
。此时
Foo.prototype.age
不受影响。

当不使用 new 操作符时,相当于一个​普通对象上的函数调用,
this
 指向了 
init
 所属的对象,即 
Foo.prototype
this.age=18
相当于对 
Foo.prototype.age
 赋值,和使用
new 操作符是有本质区别的。


小结

到这里,教程也就要告一段落了。一个 jQuery 中常见的 
css()
 方法背后涵盖了非常多的知识点,跨浏览器的兼容性也是我们此次讨论的重点,这次只是实现了一个非常简易的
css 操作插件。学问尚浅,如果有不清楚或者有错误的地方,欢迎各位留言或者提issue来帮助我改进这个小插件。

最后,完整的项目地址:https://github.com/xdlrt/css-...

求一波star~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: