Heim Web-Frontend js-Tutorial javascript 精确获取样式属性(上)_javascript技巧

javascript 精确获取样式属性(上)_javascript技巧

May 16, 2016 pm 06:37 PM
javascript

JQuery,mootools,Ext等类库在这部分实现得非常艰辛,盘根错节地动用一大堆方法,因此想把这部分抠出来难度很大。深入研究它们的实现后,根据我积累的CSS知识,终于做出一个非常简炼的版本出来。它相当于JQuery.cssCur吧,不过或许功能还丰富一些,按饮食业话说叫“加量不加价”,我的可能还应叫“加量还减价”……版本还处于Beta阶段,由于只个工具函数就不弄成类了。

复制代码 代码如下:

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)
}
}

这是函数的最原始状态,由于used value是W3C那边的人搞出来的,因此document.defaultView.getComputedStyle 基本连动也不动就解决百分之99的问题。IE那边的复杂了,虽然微软搞了style,currentStyle与runtimeStyle,但始终都没有一个与getComputedStyle相近的实现,最相近的是currentStyle,它只能取到内部样式,而且我们取值时要把CSS属性转换成驼峰风格。为了方便,我现在再把它分离出来。
复制代码 代码如下:

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

接着我们单独解决IE的透明度问题,基本各大类库都是这样做的,可见这问题多么棘手,实在要感谢微软那帮天才:
复制代码 代码如下:

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;
}

这时我们的函数就变成这样:
复制代码 代码如下:

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)
}
}

接着下来float属性问题。IE这边是styleFloat,W3C是cssFloat。解决不是问题,不过每次都要转换太麻烦了,我参照Ext的实现把它们缓存起来。
复制代码 代码如下:

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) { //意思为: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)
}
}

到最难的部分了——精确取得高度与宽度。如果用过JQuery的人都知道,John Resig是单独处理这两个属性。其实何止JQuery,其他库都为此头痛。问题的起因是如果没有内联样式或内部样式显式地设置这两个属性,我们在IE下是无法获取它们精确的值,或者获得的是百分比,空字符串,auto,inhert等让人无可奈何的东西。另,对于国人来说,px的地位是远远高于em。总总原因,让我们不能放弃这两个属性。为了取得这两个属性的精确值,我们就需要研究一下CSS继承问题了。我也已撰写相关博文《CSS的inhert与auto》来探讨这问题,没有看,请看完再回来,要不,根本是无法看下去的。
根据CSS的分类,width与height是属性于non-inherited property,由此可知,如果我们不为它设值,默认为auto。这个auto是个神奇的东东,如果是块状元素,它的宽就相当于100%,撑满父元素的内容区,当然这是在不考虑其padding,border与margin的情况下。如果是内联元素,由于不具备盒子模型,你给它设置宽与高是没有意义的,就算在火狐,返回的改过转换的精确值与你看到的情况完全不吻合,如果没有设置,直接返回auto。为了取得以px为单位的精确值,为了屏蔽块状元素与内联元素,我们需要转换思路,不过光盯着CSS属性转。这时,微软做了件好事,开发出offsetXX,clientXX与scrollXX三大家族,现在终于纳入W3C的标准。不过早在这之前,各浏览器已经跟风实现了。
在标准模式中,offsetWidth是包含padding,borderWidth与width,如果存在滚动条,它的offsetWidth也不会变,滚动条的宽度在各浏览器非常一致,都为17px,这时它就会把width减去17px,缺失的空间由滚动条补上。 offsetWidth 如果存在padding与padding,我们就要减去padding与padding 在怪癖模式下,offsetWidth等于width,而width是包含padding与borderWidth。 offsetHeight同理。 clientXX家族好理解,就是不包含borderWidth与滚动条。scrollXX家族不说了,在五大浏览器都不一致。因此,在标准模式中,要取得高与宽,首选clientXX家族,怪癖模式中,首先offsetXX家族。
这时不得不说一下怪癖模式了,别以为升到IE8,设置相应DocType就可以逃过一劫,只要你的网页太多地方不按标准写,IE也转为兼容模式中运作。这兼容模式是否等于怪癖模式就不得而已,因为IE8有多达五种渲染模式,IE5怪癖模式,IE7标准模式,IE8几乎标准模式,IE7兼容模式与支持HTML5的边缘模式。这么多模式,你以为光靠 document.compatMode == "CSS1Compat"能撑住吗?!撑不住的,幸好IE8在添加新模式的同时,又添加了一个 document.documentMode属性,真不知是喜还是悲了。因此判断是否运行于怪癖模式的代码为:
http://www.mangguo.org/x-ua-compatible-ie8-compatible-mode/
复制代码 代码如下:

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

于是我们有如下伪码:
复制代码 代码如下:

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

对比一下Ext的实现(只抠出核心部分):
复制代码 代码如下:

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 },

非常危险的做法,比Prototype的实现还差劲,因此它就不得在其他部分进行纠正,这就是为什么它的UI体积如此庞大的缘故,从侧面也突现JQuery实现手法的高超。不过JQuery的实现了也是相当复杂,如果本元素无法取得精确值,就从上级元素着手,这个遍历消耗非常严重。其中还借用了Dean Edwards的一个伟大的hack:
复制代码 代码如下:

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;
}
//此函数由Dean Edwards提供的,最早出处见下面链接
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
//注意,第二参数必须是带单位的数值,如em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc'
//百分比好像有点问题
//另,不要试图用于IE以外的浏览器
//用法:convertPixelValue($("drag4"),'10em')
var convertPixelValue = function(el, styleVal){
//保存原来的值到left与rsLeft
var style = el.style,left = style.left,rsLeft = el.runtimeStyle.left;
//下面这步是关键,
//把el.currentStyle.left代入到el.runtimeStyle.left中,激活hack
el.runtimeStyle.left = el.currentStyle.left;
//把非px单位的数值代入style.left中,如10em
style.left = styleVal || 0;
//一定要用style.pixelLeft去取,要不数值是不准确
//如果我们用style.pixelLeft会得160,用style.left会得到150px
//如果都已转换好,但style.left是不准确的。
var px = style.pixelLeft;
style.left = left;//还原数据
el.runtimeStyle.left = rsLeft;//还原数据
return px;
}

这个hack是用于将em、pc、pt、cm、in、ex等单位转换为px的,当然不包括百分比。
再回来看我的getWidth函数,主要问题是获取padding-left与padding-right的值。在标准浏览器,我们用getComputedStyle可以轻而易举地获取经过转换的精确值,单位为px。在IE中,如果你给它的值为2em,它就返回2em,很懒。在《CSS的inherit与auto》一文,我也指出了,padding为non-inherited property,这就不用处理inhert这个无厘头的值;在auto列表中,padding也不在列,减少了处理auto这个模糊值的风险;加之是可度量单位,各浏览器都很厚道地设置默认值为0px,换言之,我们需要的做的事是,当值的单位不为px,我们把它转换为px。我们把Dean Edwards的hack整合到我们的主函数getStyle中即可。如果padding-left是百分比,我们就取其父元素的width乘以百分比即可。总而言之,遍历的层次与数算的次数都被压缩最少。从这方面说,我在这方面的处理比JQuery优胜得多(JQuery连border,margin都列入计算范围,而border与margin在IE中存在模糊值,这就逼使JQuery动不动就往上计算父元素,换言之,需要重复计算其父元素的属性五次;我最多为三次,怪异模式下只需一次)。有时在IE中取得的值,比拥有getComputedStyle的火狐还精确(不过,好像精确过头,自己用toFixed调整精确度)。
复制代码 代码如下:

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)){
//转换可度量的值
if(/(em|pt|mm|cm|pc|in|ex|rem|vw|vh|vm|ch|gr)$/.test(value)){
return convertPixelValue(el,value);
}
//转换百分比
if(/%/.test(value)){
return parseFloat(getStyle(el.parentNode,"width")) * parseFloat(value) /100 + "px"
}
}
return value;//如 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
复制代码 代码如下:

//转换border的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];
}

再看top,left,right与bottom,想不到firefox,safari都会偷懒,会返回auto,只有opera老老实实地返回精确值。解决办法也很简单,因为微软一个好用的函数已被所有浏览器支持了。
复制代码 代码如下:

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

嗯,文章已经很长很长,剩下的部分下次再讲。
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

So implementieren Sie ein Online-Spracherkennungssystem mit WebSocket und JavaScript So implementieren Sie ein Online-Spracherkennungssystem mit WebSocket und JavaScript Dec 17, 2023 pm 02:54 PM

So implementieren Sie mit WebSocket und JavaScript ein Online-Spracherkennungssystem. Einführung: Mit der kontinuierlichen Weiterentwicklung der Technologie ist die Spracherkennungstechnologie zu einem wichtigen Bestandteil des Bereichs der künstlichen Intelligenz geworden. Das auf WebSocket und JavaScript basierende Online-Spracherkennungssystem zeichnet sich durch geringe Latenz, Echtzeit und plattformübergreifende Eigenschaften aus und hat sich zu einer weit verbreiteten Lösung entwickelt. In diesem Artikel wird erläutert, wie Sie mit WebSocket und JavaScript ein Online-Spracherkennungssystem implementieren.

WebSocket und JavaScript: Schlüsseltechnologien zur Implementierung von Echtzeitüberwachungssystemen WebSocket und JavaScript: Schlüsseltechnologien zur Implementierung von Echtzeitüberwachungssystemen Dec 17, 2023 pm 05:30 PM

WebSocket und JavaScript: Schlüsseltechnologien zur Realisierung von Echtzeit-Überwachungssystemen Einführung: Mit der rasanten Entwicklung der Internet-Technologie wurden Echtzeit-Überwachungssysteme in verschiedenen Bereichen weit verbreitet eingesetzt. Eine der Schlüsseltechnologien zur Erzielung einer Echtzeitüberwachung ist die Kombination von WebSocket und JavaScript. In diesem Artikel wird die Anwendung von WebSocket und JavaScript in Echtzeitüberwachungssystemen vorgestellt, Codebeispiele gegeben und deren Implementierungsprinzipien ausführlich erläutert. 1. WebSocket-Technologie

Verwendung von JavaScript und WebSocket zur Implementierung eines Echtzeit-Online-Bestellsystems Verwendung von JavaScript und WebSocket zur Implementierung eines Echtzeit-Online-Bestellsystems Dec 17, 2023 pm 12:09 PM

Einführung in die Verwendung von JavaScript und WebSocket zur Implementierung eines Online-Bestellsystems in Echtzeit: Mit der Popularität des Internets und dem Fortschritt der Technologie haben immer mehr Restaurants damit begonnen, Online-Bestelldienste anzubieten. Um ein Echtzeit-Online-Bestellsystem zu implementieren, können wir JavaScript und WebSocket-Technologie verwenden. WebSocket ist ein Vollduplex-Kommunikationsprotokoll, das auf dem TCP-Protokoll basiert und eine bidirektionale Kommunikation zwischen Client und Server in Echtzeit realisieren kann. Im Echtzeit-Online-Bestellsystem, wenn der Benutzer Gerichte auswählt und eine Bestellung aufgibt

So implementieren Sie ein Online-Reservierungssystem mit WebSocket und JavaScript So implementieren Sie ein Online-Reservierungssystem mit WebSocket und JavaScript Dec 17, 2023 am 09:39 AM

So implementieren Sie ein Online-Reservierungssystem mit WebSocket und JavaScript. Im heutigen digitalen Zeitalter müssen immer mehr Unternehmen und Dienste Online-Reservierungsfunktionen bereitstellen. Es ist von entscheidender Bedeutung, ein effizientes Online-Reservierungssystem in Echtzeit zu implementieren. In diesem Artikel wird erläutert, wie Sie mit WebSocket und JavaScript ein Online-Reservierungssystem implementieren, und es werden spezifische Codebeispiele bereitgestellt. 1. Was ist WebSocket? WebSocket ist eine Vollduplex-Methode für eine einzelne TCP-Verbindung.

JavaScript und WebSocket: Aufbau eines effizienten Echtzeit-Wettervorhersagesystems JavaScript und WebSocket: Aufbau eines effizienten Echtzeit-Wettervorhersagesystems Dec 17, 2023 pm 05:13 PM

JavaScript und WebSocket: Aufbau eines effizienten Echtzeit-Wettervorhersagesystems Einführung: Heutzutage ist die Genauigkeit von Wettervorhersagen für das tägliche Leben und die Entscheidungsfindung von großer Bedeutung. Mit der Weiterentwicklung der Technologie können wir genauere und zuverlässigere Wettervorhersagen liefern, indem wir Wetterdaten in Echtzeit erhalten. In diesem Artikel erfahren Sie, wie Sie mit JavaScript und WebSocket-Technologie ein effizientes Echtzeit-Wettervorhersagesystem aufbauen. In diesem Artikel wird der Implementierungsprozess anhand spezifischer Codebeispiele demonstriert. Wir

Einfaches JavaScript-Tutorial: So erhalten Sie den HTTP-Statuscode Einfaches JavaScript-Tutorial: So erhalten Sie den HTTP-Statuscode Jan 05, 2024 pm 06:08 PM

JavaScript-Tutorial: So erhalten Sie HTTP-Statuscode. Es sind spezifische Codebeispiele erforderlich. Vorwort: Bei der Webentwicklung ist häufig die Dateninteraktion mit dem Server erforderlich. Bei der Kommunikation mit dem Server müssen wir häufig den zurückgegebenen HTTP-Statuscode abrufen, um festzustellen, ob der Vorgang erfolgreich ist, und die entsprechende Verarbeitung basierend auf verschiedenen Statuscodes durchführen. In diesem Artikel erfahren Sie, wie Sie mit JavaScript HTTP-Statuscodes abrufen und einige praktische Codebeispiele bereitstellen. Verwenden von XMLHttpRequest

So verwenden Sie insertBefore in Javascript So verwenden Sie insertBefore in Javascript Nov 24, 2023 am 11:56 AM

Verwendung: In JavaScript wird die Methode insertBefore() verwendet, um einen neuen Knoten in den DOM-Baum einzufügen. Diese Methode erfordert zwei Parameter: den neuen Knoten, der eingefügt werden soll, und den Referenzknoten (d. h. den Knoten, an dem der neue Knoten eingefügt wird).

JavaScript und WebSocket: Aufbau eines effizienten Echtzeit-Bildverarbeitungssystems JavaScript und WebSocket: Aufbau eines effizienten Echtzeit-Bildverarbeitungssystems Dec 17, 2023 am 08:41 AM

JavaScript ist eine in der Webentwicklung weit verbreitete Programmiersprache, während WebSocket ein Netzwerkprotokoll für die Echtzeitkommunikation ist. Durch die Kombination der leistungsstarken Funktionen beider können wir ein effizientes Echtzeit-Bildverarbeitungssystem erstellen. In diesem Artikel wird erläutert, wie dieses System mithilfe von JavaScript und WebSocket implementiert wird, und es werden spezifische Codebeispiele bereitgestellt. Zunächst müssen wir die Anforderungen und Ziele des Echtzeit-Bildverarbeitungssystems klären. Angenommen, wir haben ein Kameragerät, das Bilddaten in Echtzeit sammeln kann

See all articles