Home  >  Article  >  Web Front-end  >  JavaScript accurately obtains style attributes (Part 1)_javascript skills

JavaScript accurately obtains style attributes (Part 1)_javascript skills

WBOY
WBOYOriginal
2016-05-16 18:37:21807browse

JQuery, mootools, Ext and other class libraries are very difficult to implement in this part. They use a lot of intricate methods, so it is very difficult to extract this part. After in-depth study of their implementation, based on my accumulated CSS knowledge, I finally made a very concise version. It is equivalent to JQuery.cssCur, but it may have more functions. According to the catering industry, it is called "increase the amount without increasing the price". Mine may be called "increase the amount but reduce the price"... The version is still in the Beta stage, because it is only one Utility functions are not made into classes.

Copy code The code is as follows:

var getStyle = function(el, style){
if(! "v1"){
style = style.replace(/-(w)/g, function(all, letter){
return letter.toUpperCase();
});
return el.currentStyle[style];
}else{
return document.defaultView.getComputedStyle(el, null).getPropertyValue(style)
}
}

This is the original state of the function. Since the used value was created by people from W3C, document.defaultView.getComputedStyle basically solves 99% of the problems without moving. IE is more complicated. Although Microsoft has developed style, currentStyle and runtimeStyle, there is still no implementation similar to getComputedStyle. The closest thing is currentStyle, which can only get the internal style, and when we get the value, we need to use CSS attributes. Convert to camelCase style. For convenience, I will separate it now.
Copy code The code is as follows:

var camelize = function(attr){
return attr.replace(/-(w)/g, function(all, letter){
return letter.toUpperCase();
});
}

Let’s follow To solve the transparency problem of IE alone, basically all major libraries do this. It shows how difficult this problem is. I really want to thank the geniuses at Microsoft:
Copy code The code is as follows:

var getIEOpacity = function(el){
var filter;
if(!!window.XDomainRequest){
filter = el.style.filter.match(/progid:DXImageTransform.Microsoft.Alpha(.?opacity=(.*).?)/i);
}else{
filter = el.style.filter. match(/alpha(opacity=(.*))/i);
}
if(filter){
var value = parseFloat(filter[1]);
if (!isNaN( value)) {
return value ? value / 100 : 0;
}
}
return 1;
}

At this time our function becomes It looks like this:
Copy code The code is as follows:

var getStyle = function(el, style) {
if(! "v1"){
if(style == "opacity"){
return getIEOpacity(el)
}
return el.currentStyle[camelize(style)] ;
}else{
return document.defaultView.getComputedStyle(el, null).getPropertyValue(style)
}
}

Then comes the float attribute issue. IE is styleFloat, W3C is cssFloat. The solution is not a problem, but it is too troublesome to convert every time. I cache them based on the implementation of Ext.
Copy code The code is as follows:

var propCache = [];
var propFloat = ! "v1" ? 'styleFloat' : 'cssFloat';
var camelize = function(attr){
return attr.replace(/-(w)/g, function(all, letter){
return letter.toUpperCase();
});
}
var memorize = function(prop) { //Means: check out form cache
return propCache[prop] || (propCache[ prop] = prop == 'float' ? propFloat : camelize(prop));
}
var getIEOpacity = function(el){
//************ ***************************
}
var getStyle = function (el, style){
if( ! "v1"){
if(style == "opacity"){
return getIEOpacity(el)
}
return el.currentStyle[memorize(style)];
}else {
if(style == "float"){
style = propFloat;
}
return document.defaultView.getComputedStyle(el, null).getPropertyValue(style)
}
}

Now comes the hard part - getting the height and width accurately. Anyone who has used JQuery knows that John Resig handles these two properties separately. In fact, not just JQuery, other libraries have this headache. The cause of the problem is that if there is no inline style or internal style to explicitly set these two properties, we cannot get their precise values ​​​​under IE, or what we get is percentage, empty string, auto, inherit, etc., which is helpless. something. In addition, for Chinese people, the status of px is much higher than that of em. There are total reasons why we cannot give up these two attributes. In order to obtain the precise values ​​of these two properties, we need to study the CSS inheritance issue. I have also written a related blog post "CSS Inhert and Auto" to discuss this issue. If you have not read it, please come back after reading it. Otherwise, you will not be able to read it at all.
According to the classification of CSS, width and height are non-inherited properties. From this, it can be seen that if we do not set a value for it, the default is auto. This auto is a magical thing. If it is a block element, its width is equivalent to 100%, filling the content area of ​​the parent element. Of course, this is without considering its padding, border and margin. If it is an inline element, since it does not have a box model, it is meaningless for you to set the width and height. Even in Firefox, the exact value of the conversion returned is completely inconsistent with what you see. If there is no setting, directly Return auto. In order to get the exact value in px, in order to block block elements and inline elements, we need to change our thinking, but just focus on CSS properties. At this time, Microsoft did a good thing and developed the three major families of offsetXX, clientXX and scrollXX, which are now finally included in the W3C standard. But long before that, browsers had already followed suit.
In standard mode, offsetWidth includes padding, borderWidth and width. If there is a scroll bar, its offsetWidth will not change. The width of the scroll bar is very consistent in each browser, 17px. At this time, it will Subtract 17px from the width and fill in the missing space with the scroll bar. offsetWidth If padding and padding exist, we have to subtract padding and padding. In quirks mode, offsetWidth is equal to width, and width includes padding and borderWidth. The same applies to offsetHeight. The clientXX family is easy to understand, but it does not include borderWidth and scroll bars. Not to mention the scrollXX family, it is inconsistent in the five major browsers. Therefore, in standard mode, to obtain height and width, the clientXX family is preferred, and in quirks mode, the offsetXX family is preferred first.
At this time, I have to talk about quirk mode. Don’t think that you can escape the disaster by upgrading to IE8 and setting the corresponding DocType. As long as there are too many parts of your web page that are not written according to standards, IE will also switch to compatibility mode. Whether this compatibility mode is equal to quirks mode is questionable, because IE8 has as many as five rendering modes, IE5 quirks mode, IE7 standards mode, IE8 almost standards mode, IE7 compatibility mode and edge mode that supports HTML5. With so many modes, do you think you can survive just by document.compatMode == "CSS1Compat"? ! I can't stand it. Fortunately, while adding a new mode, IE8 also added a document.documentMode attribute. I don't know whether I am happy or sad. Therefore, the code to determine whether it is running in quirks mode is:
http://www.mangguo.org/x-ua-compatible-ie8-compatible-mode/
Copy code The code is as follows:

var isQuirk = (document.documentMode) ? (document.documentMode==5) ? true :
false : ( (document.compatMode=="CSS1Compat") ? false : true);

So we have the following pseudocode:
Copy code The code is as follows:

var getWidth = function(el){
if(isQuirk){
return el.offsetWidth
}else{
return el.clientWidth - parseFloat(getStyle(el, "padding-left"))- parseFloat(getStyle(el, "padding-right"))
}
}

Compare the implementation of Ext (only the core part is cut out):
Copy the code The code is as follows:

getWidth : function(contentWidth){
var me = this,
dom = me.dom,
w = MATH.max(dom.offsetWidth, dom.clientWidth) || 0;
w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
return w < 0 ? 0 : w;
},

A very dangerous approach, which is worse than the implementation of Prototype, so it cannot be corrected in other parts. This is why its UI is so large in size. It also highlights the superb JQuery implementation method from the side. However, the implementation of JQuery is also quite complicated. If the exact value of this element cannot be obtained, start from the superior element. This traversal consumes a lot of money. It also borrowed a great hack from Dean Edwards:
Copy the code The code is as follows:

var convertPixelValue = function(el, value){
var style = el.style,left = style.left,rsLeft = el.runtimeStyle.left;
el.runtimeStyle.left = el.currentStyle.left;
style.left = value || 0;
var px = style.pixelLeft;
style.left = left;
el.runtimeStyle.left = rsLeft;
return px;
}
//This function is provided by Dean Edwards, the earliest source can be found in the link below
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
//Note that the second parameter must be a numerical value with a unit, such as em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc'
//Percentage There seems to be some problem
//Also, don’t try to use it with browsers other than IE
//Usage: convertPixelValue($("drag4"),'10em')
var convertPixelValue = function(el, styleVal ){
//Save the original values ​​to left and rsLeft
var style = el.style,left = style.left,rsLeft = el.runtimeStyle.left;
//The following step is the key,
// Substitute el.currentStyle.left into el.runtimeStyle.left to activate the hack
el.runtimeStyle.left = el.currentStyle.left;
// Substitute non-px unit values ​​into style .left, such as 10em
style.left = styleVal || 0;
//Be sure to use style.pixelLeft to get it, otherwise the value will be inaccurate
//If we use style.pixelLeft it will It gets 160, and using style.left will get 150px
//If everything has been converted, style.left is inaccurate.
var px = style.pixelLeft;
style.left = left;//Restore data
el.runtimeStyle.left = rsLeft;//Restore data
return px;
}

This hack is used to convert em, pc, pt, cm, in, ex and other units into px, excluding percentages of course.
Come back and look at my getWidth function. The main problem is to get the values ​​of padding-left and padding-right. In standard browsers, we can easily get the converted exact value in px using getComputedStyle. In IE, if you give it a value of 2em, it returns 2em, which is lazy. In the article "Inherit and Auto of CSS", I also pointed out that padding is a non-inherited property, which means there is no need to deal with the nonsensical value of inherit; in the auto list, padding is not listed, which reduces the need to deal with the fuzzy value of auto. Risk; in addition, it is a measurable unit, and each browser is kind enough to set the default value to 0px. In other words, what we need to do is, when the unit of the value is not px, we convert it to px. We just integrate Dean Edwards' hack into our main function getStyle. If padding-left is a percentage, we just take the width of its parent element and multiply it by the percentage. All in all, the number of levels traversed and the number of calculations are minimized. In this regard, my handling of this aspect is much better than that of JQuery (JQuery even includes border and margin in the calculation range, but border and margin have fuzzy values ​​​​in IE, which forces JQuery to calculate the parent value at every turn. The element, in other words, needs to recalculate its parent's properties five times; up to three times for me, only once in weird mode). Sometimes the value obtained in IE is more accurate than Firefox with getComputedStyle (however, it seems to be too accurate, so use toFixed to adjust the accuracy yourself).
Copy code The code is as follows:

var getStyle = function (el, style){
if(! "v1"){
if(style == "opacity"){
return getIEOpacity(el)
}
var value = el.currentStyle[memorize(style)];
if (/^(height|width)$/.test(style)){
var values ​​= (style == 'width') ? ['left', 'right'] : ['top' , 'bottom'], size = 0;
if(isQuirk){
return el[camelize("offset-" style)]
}else{
var client = parseFloat(el[camelize ("client-" style)]),
paddingA = parseFloat(getStyle(el, "padding-" values[0])),
paddingB = parseFloat(getStyle(el, "padding-" values[1 ]));
return (client - paddingA - paddingB) "px";
}
}
if(!/^d px$/.test(value)){
/ /Convert measurable values
if(/(em|pt|mm|cm|pc|in|ex|rem|vw|vh|vm|ch|gr)$/.test(value)){
return convertPixelValue(el,value);
}
//Conversion percentage
if(/%/.test(value)){
return parseFloat(getStyle(el.parentNode,"width" )) * parseFloat(value) /100 "px"
}
}
return value;//such as 0px
}else{
if(style == "float"){
style = propFloat;
}
return document.defaultView.getComputedStyle(el, null).getPropertyValue(style)
}
}

说多无谓,我们测试一下吧。
父元素
子元素


window.onload = function(){
alert(getStyle(_("text"),"width"))
alert(getStyle(_("text2"),'width'))
alert(getStyle(_("text2"),'padding-left'))
};

[Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]

发现在IE取得值太精确了,比火狐还准确,不过我也不打算在程序内部设置取精度的运算,因为我不确定现实中究竟会向上遍历多少次。在某一父元素的padding与width取精度,很可能会严重影响其最终值的精度。基本上,width与height与padding的取值就到此为止,我们再来看盒子模型的两个东西:margin与border。
margin的auto通常为0px,但在IE6下,当我们将其父元素的text-align设为center,它不但把文本居中,连块级元素本身也忧患中了,这是IE6居中布局的基石。很难说这是BUG,就像IE5的怪癖模式的盒子模型那样,很符合人通常的思维习惯,但是较难实现,加之W3C是偏袒被微软阴死的网景,于是网景的盒子模型成为标准了。IE6庞大的用户基础不容忽视,我们不能随便给0px了事。我也说了,auto有个对称性,因此好办,我们求出其父元素的width然后减于其offsetWidth再除以2就行了。因为offsetWidth是针对于offsertParent的,因此我们用时临时把其父元素的position设为relative,让子元素来得offsetWidth后,再将父元素的position还原。
复制代码 代码如下:

//转换margin的auto
if(/^(margin).+/.test(style) && value == "auto"){
var father = el.parentNode;
if(/MSIE 6/.test(navigator.userAgent) && getStyle(father,"text-align") == "center"){
var fatherWidth = parseFloat(getStyle(father,"width")),
_temp = getStyle(father,"position");
father.runtimeStyle.postion = "relative";
var offsetWidth = el.offsetWidth;
father.runtimeStyle.postion = _temp;
return (fatherWidth - offsetWidth)/2 + "px";
}
return "0px";
}

borderWidth的默认值为medium,即使它为0px,但如果我们显式地设置其宽为medium呢?!它就不为0px了。这个比较恶心,我们需要判定这值是我们自己加上的还是浏览器的默认值。不过我们发现如果是默认的,其border-XX-style为none。另,除了medium外,还存在两个模糊值thin与thick。它们在浏览器的精确值见下表:
IE8 ,firefox等标准浏览器 IE4-7
thin 1px 2px
medium 3px 4px
thick 5px 6px
Copy code The code is as follows:

//Convert border to thin medium thick
if(/^ (border). (width)$/.test(style)){
var s = style.replace("width","style"),
b = {
thin:["1px" ,"2px"],
medium:["3px","4px"],
thick:["5px","6px"]
};
if(value == "medium " && getStyle(el,s) == "none"){
return "0px";
}
return !!window.XDomainRequest ? b[value][0] : b[value][ 1];
}

Looking at top, left, right and bottom, I didn’t expect that firefox and safari would be lazy and return auto. Only opera returns the exact value honestly. The solution is also very simple, because a useful function from Microsoft is already supported by all browsers.
Copy code The code is as follows:

//Convert top|left|right|bottom's auto
if(/(top|left|right|bottom)/.test(style) && value == "auto"){
return el.getBoundingClientRect()[style]
}

Well, the article is already very long, I will talk about the rest next time.
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:javascript bind binding function code_javascript skillsNext article:javascript bind binding function code_javascript skills

Related articles

See more