Javascript precisely retrieves style attributes (on)

Source: Internet
Author: User
Tags css inherit

JQuery, mootools, Ext, and other class libraries are very difficult to implement in this part. It is very difficult to compile this part. After in-depth research on their implementation, I finally made a very concise version based on my accumulated CSS knowledge. The answer is quite similar to jquery.css Cur, but it may be richer in features. According to the catering industry, the answer is "increase without price". I may also call "Increase and price reduction "...... The version is still in Beta stage, because only tool functions are not made into classes.
Copy codeThe 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. Because used value is developed by W3C, document. defaultView. getComputedStyle can solve the problem of 99 percent even if it does not move. IE is complicated. Although Microsoft is engaged in style, currentStyle and runtimeStyle, there is never any implementation similar to getComputedStyle. The closest is currentStyle, which can only get internal styles, in addition, we need to convert the CSS attribute into a camper style. For convenience, I will separate it now.
Copy codeThe Code is as follows:
Var camelize = function (attr ){
Return attr. replace (/\-(\ w)/g, function (all, letter ){
Return letter. toUpperCase ();
});
}

Next, we will solve the issue of IE transparency separately. Basically all major libraries are doing this. We can see how tricky this problem is. I really want to thank Microsoft for its talents:
Copy codeThe 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? Values/100: 0;
}
}
Return 1;
}

Then our function becomes like this:
Copy codeThe 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)
}
}

Next we will discuss the float attribute issue. In IE, styleFloat and W3C are cssFloat. Solving the problem is not a problem, but it is too troublesome to switch every time. I will cache them by referring to the Ext implementation.
Copy codeThe 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) {// meaning: 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)
}
}

To the most difficult part-precisely obtain the height and width. If anyone who has used JQuery knows that John Resig handles these two attributes separately. In fact, JQuery has a headache for other libraries. The cause of the problem is that if no inline style or internal style explicitly sets these two attributes, we cannot get their exact values in IE, or get the percentage, null string, auto, inhert, and other helpless things. In addition, for Chinese people, the position of px is much higher than that of em. The total reason is that we cannot discard these two attributes. To obtain the exact values of these two attributes, we need to study the CSS Inheritance Problem. I have also written the relevant blog "CSS inhert and auto" to discuss this issue. I didn't read it. Please come back after reading it. Otherwise, I cannot read it at all.
According to the CSS classification, width and height are attributes in non-inherited property. We can see that if we do not set a value for them, the default value is auto. This auto is a magic stuff. If it is a block element, its width is equivalent to 100%, and the content zone of the parent element is full. Of course, this is not considering its padding, in the case of border and margin. If it is an inline element and does not have a box model, it makes no sense to set the width and height for it, even in Firefox, the exact value of the returned transformation is totally different from the one you see. If no value is set, auto is returned directly. In order to obtain accurate values in px units, we need to convert ideas to shield block and inline elements, but just stare at CSS attribute conversion. At this time, Microsoft has done a good job and developed the offsetXX, clientXX, and scrollXX families. Now, they are finally included in W3C standards. But as early as this day, various browsers have followed suit.
In standard mode, offsetWidth includes padding, borderWidth, and width. If a scroll bar exists, its offsetWidth will not change. The width of the scroll bar is very consistent in different browsers and is 17px, in this case, the width is subtracted from 17px, and the missing space is filled by the scroll bar. If there is padding and padding in offsetWidth, We need to subtract padding and padding. In the geek mode, offsetWidth equals width, while width includes padding and borderWidth. The same applies to offsetHeight. The clientXX family is easy to understand, that is, it does not contain borderWidth and scroll bar. The scrollXX family is not mentioned, and the five browsers are inconsistent. Therefore, in the standard mode, the clientXX family is the first choice to obtain the height and width. In the strange mode, the offsetXX family is the first choice.
At this time, I have to talk about the strange pattern. Don't think that it will be upgraded to IE8. Setting the corresponding DocType will escape. As long as you have too many webpages and do not write according to the standard, IE will also be converted into compatible mode. This compatibility mode is not equal to the geek mode, because IE8 has up to five rendering modes, IE5 geek mode, IE7 standard mode, and IE8 almost standard mode, IE7 compatibility mode and HTML5 edge mode. In this multi-mode, Do you think document. compatMode = "CSS1Compat" alone can hold on ?! Fortunately, IE8 added a document.doc umentMode attribute while adding a new mode. Therefore, the code used to determine whether to run in the geek mode is:
Http://www.mangguo.org/x-ua-compatible-ie8-compatible-mode/
Copy codeThe Code is as follows:
Var isQuirk = (document.doc umentMode )? (Document.doc umentMode = 5 )? True:
False: (document. compatMode = "CSS1Compat ")? False: true );

So we have the following pseudo code:
Copy codeThe 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 extract the core part ):
Copy codeThe 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 is worse than Prototype implementation, so it cannot be corrected in other parts, which is why its UI size is so large, the JQuery implementation method is also superb. However, the implementation of JQuery is quite complicated. If the element cannot obtain the exact value, start with the upper-level element. This traversal consumes a lot. It also borrowed a great hack from Dean Edwards:
Copy codeThe 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. For the earliest sources, see the following link.
// Http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
// Note that the second parameter must be a unit value, such as em ', 'ex', 'cm ', 'mm', 'in', 'pt', and 'pc'
// The percentage seems to be a problem.
// In addition, do not try to use 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,
// Place el. currentStyle. left into el. runtimeStyle. left to activate hack.
El. runtimeStyle. left = el. currentStyle. left;
// Place a non-px value in style. left, for example, 10em.
Style. left = styleVal | 0;
// You must use style. pixelLeft to retrieve the image. Otherwise, the value is inaccurate.
// If we use style. pixelLeft, we get 160. If we use style. left, we get PX.
// If all of them have been converted, but style. left is not accurate.
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 to px, excluding the percentage.
When I look back at my getWidth function, the main problem is to get the values of padding-left and padding-right. In the standard browser, we can use getComputedStyle to easily obtain the precise converted value, in px. In IE, if you give it a value of 2em, it returns 2em, very lazy. In the article "CSS inherit and auto", I also pointed out that padding is a non-inherited property, so you do not need to process the value of inhert without a response header. In the auto list, padding is not included in the column, which reduces the risk of processing the Fuzzy Value of auto. In addition, it is a measurable unit, and each browser has a thick and genuine default value of 0 PX. In other words, what we need to do is to convert the unit of value to px instead of px. We can integrate Dean Edwards's hack into our main function getStyle. If padding-left is a percentage, We can multiply the width of its parent element by the percentage. All in all, the level and number of computations of traversal are compressed at least. In this regard, I am much better at this aspect than JQuery (JQuery is included in both border and margin, while border and margin have fuzzy values in IE, this forces JQuery to calculate the parent element easily. In other words, the attribute of the parent element needs to be repeated for five times. I can calculate the parent element three times at most, only once in the weird mode ). Sometimes the value obtained in IE is more accurate than that of Firefox with getComputedStyle (however, it seems that it is too accurate to adjust the accuracy with toFixed ).
Copy codeThe 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 | VL | 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; // For example, 0px
} Else {
If (style = "float "){
Style = propFloat;
}
Return document. defaultView. getComputedStyle (el, null). getPropertyValue (style)
}
}

Let's test it.
<Div id = "text" style = "width: 3in; height: 180px; background: #8080c0; padding: 2%;"> parent Element
<Div id = "text2" style = "width: 78%; height: 4em; padding: 1%; background: red; border: 1px solid red"> child element </div>
</Div>
Window. onload = function (){
Alert (getStyle (_ ("text"), "width "))
Alert (getStyle (_ ("text2"), 'width '))
Alert (getStyle (_ ("text2"), 'padding-left '))
};
<! Doctype html> <ptml dir = "ltr" lang = "zh-CN"> <pead> <meta charset = "UTF-8"/> <meta http-equiv = "X-UA -Compatible "content =" IE = 8 "> <style type =" text/css "> </style> <title> exact value </title> </pead> <body> parent element and child element </body> </ptml>
[Ctrl + A select all Note: If you need to introduce external Js, You need to refresh it to execute]
I found that the value obtained in IE is too accurate and more accurate than Firefox, but I am not planning to set the precision calculation in the program, because I am not sure how many times in reality will traverse up. The precision of the padding and width values of a parent element may be seriously affected. Basically, the values of width, height, and padding are so far. Let's look at two things of the Box Model: margin and border.
The auto of margin is usually 0 PX, but in IE6, when we set the text-align of its parent element as the center, it not only centers the text, even Block-level elements are in trouble, which is the cornerstone of IE6's center layout. It is hard to say that this is a BUG. Just like the box model of IE5's geek model, it is very suitable for people's common habits of thinking, but it is difficult to implement it. In addition, W3C is biased towards the Internet scene where Microsoft is overcast, as a result, the box model of Netscape becomes a standard. The huge user base of IE6 cannot be ignored. We cannot give 0 PX at will. I also said that auto has a symmetry, so we can easily find the width of its parent element and then subtract it from its offsetWidth and divide it by 2. Because offsetWidth is for offsertParent, we temporarily set the position of the parent element to relative so that the child element can come with offsetWidth and then restore the position of the parent element.
Copy codeThe Code is as follows:
// Convert the auto of margin
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 ";
}

The default value of borderWidth is medium. Even if it is 0px, what if we explicitly set its width to medium ?! It is not 0px. This is disgusting. We need to determine whether this value is added by ourselves or the default value of the browser. However, we found that if it is the default, its border-XX-style is none. In addition to medium, two fuzzy values thin and thick exist. Their exact values in the browser are shown in the following table:

Standard browsers such as IE8 and firefox IE4-7
Thin 1px 2px
Medium 3px 4px
Thick 5px 6px
Copy codeThe Code is as follows:
// Convert the thin medium thick of border
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];
}

Let's look at top, left, right, and bottom again. We can't think of firefox or safari as laziness and will return auto. Only opera honestly returns the exact value. The solution is also very simple, because a good function of Microsoft has been supported by all browsers.
Copy codeThe 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 very long, and the rest will be discussed later.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.