ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptはスタイル属性を正確に取得する(前編)_JavaScriptスキル

JavaScriptはスタイル属性を正確に取得する(前編)_JavaScriptスキル

WBOY
WBOYオリジナル
2016-05-16 18:37:21832ブラウズ

JQuery、mootools、Ext などのクラス ライブラリは、この部分の実装が非常に困難で、多くの複雑なメソッドが使用されているため、この部分を抽出するのは非常に困難です。蓄積された CSS の知識に基づいて実装を徹底的に研究した後、最終的に非常に簡潔なバージョンを作成しました。 JQuery.cssCurと同等ですが、ケータリング業界的には「量を増やして値段を下げる」と言うのかもしれませんが…。クラス化されていないユーティリティ関数が 1 つだけあるため、バージョンはまだベータ段階です。

コードをコピーします コードは次のとおりです:

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

これは関数の元の状態です。使用される値は W3C の人々によって作成されたため、document.defaultView.getComputedStyle は基本的に移動せずに問題の 99% を解決します。 IE はさらに複雑です。Microsoft は、currentStyle と runtimeStyle というスタイルを開発しましたが、getComputedStyle に類似した実装はまだありません。これは内部スタイルのみを取得でき、値を取得するときに CSS を使用する必要があります。属性をキャメルケーススタイルに変換します。便宜上、ここで分けておきます。
コードをコピー コードは次のとおりです。

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

IE の透明性の問題を単独で解決するには、基本的にすべての主要なライブラリがこれを行っています。これは、Microsoft の天才に心から感謝したいと思います:

コードをコピー コードは次のとおりです。
var getIEOpacity = function(el){
var filter(!!window; .XDomainRequest){
filter = el.style.filter.match(/progid:DXImageTransform.Microsoft.Alpha(.?opacity=(.*).?)/i);フィルター = el.style.filter.match(/alpha(opacity=(.*))/i);
}
if(filter){
var value = parseFloat(filter[1]);
if (!isNaN(value)) {
戻り値 ? 値 / 100 : 0;
}
}


この時点で関数は次のようになります:



コードをコピー

コードは次のとおりです: 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 remember = function(prop) { // 意味: フォーム キャッシュをチェックアウト
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 がこれら 2 つのプロパティを別々に処理することを知っています。実際、JQuery だけでなく、他のライブラリもこの問題を抱えています。問題の原因は、これら 2 つのプロパティを明示的に設定するためのインライン スタイルまたは内部スタイルがない場合、IE では正確な値を取得できないか、取得されるのはパーセント、空の文字列、自動、継承、など、どうしようもないものです。さらに、中国人にとって、px のステータスは em よりもはるかに高いです。これら 2 つの属性を放棄できないのには、十分な理由があります。これら 2 つのプロパティの正確な値を取得するには、CSS 継承の問題を調べる必要があります。この問題については、関連するブログ投稿「CSS Inhert and Auto」も作成しました。まだ読んでいない場合は、読んでから戻ってください。
CSS の分類によれば、幅と高さは継承されないプロパティであることがわかります。値を設定しない場合、デフォルトは auto です。この自動は魔法のようなもので、ブロック要素の場合、その幅は親要素のコンテンツ領域を埋めるのに 100% に相当します。もちろん、これはパディング、ボーダー、マージンを考慮しません。インライン要素の場合、ボックスモデルがないため、Firefox であっても、返される変換の正確な値は、表示される値と完全に一致しません。設定なし、直接 auto を返します。 px の正確な値を取得するには、ブロック要素とインライン要素をブロックするには、考え方を変える必要がありますが、CSS プロパティに注目するだけです。このとき、Microsoft は良いことをして、offsetXX、clientXX、scrollXX の 3 つの主要なファミリを開発し、最終的に W3C 標準に組み込まれました。しかし、それよりずっと前に、ブラウザもすでにこれに追随していました。
標準モードでは、offsetWidth にはパディング、borderWidth および width が含まれます。スクロール バーがある場合、その offsetWidth は各ブラウザで非常に一定であり、現時点では 17px です。幅から 17px を削除し、足りないスペースをスクロール バーで埋めます。 offsetWidth パディングとパディングが存在する場合は、パディングとパディングを減算する必要があります。quirks モードでは、offsetWidth は width と等しく、width にはパディングと borderWidth が含まれます。 offsetHeight にも同じことが当てはまります。 clientXX ファミリは理解しやすいですが、borderWidth とスクロール バーが含まれていません。 scrollXXファミリーはもちろん、5大ブラウザでも一貫性がありません。したがって、標準モードでは高さと幅を取得するために clientXX ファミリが優先され、quirks モードでは offsetXX ファミリが最初に優先されます。
現時点では、Web ページに互換性のない部分が多すぎる限り、IE8 にアップグレードして、対応する DocType を設定することでこの惨状を回避できるとは考えないでください。標準に従って記述されている場合、IE も互換モードに切り替わります。この互換モードが互換モードと同等かどうかは疑問です。IE8 には、IE5 互換モード、IE7 標準モード、IE8 ほぼ標準モード、IE7 互換モード、および HTML5 をサポートするエッジ モードという 5 つものレンダリング モードがあるからです。これだけ多くのモードがある中で、document.compatMode == "CSS1Compat" だけで生き残れると思いますか? !幸いなことに、IE8 には新しいモードが追加されると同時に、documentMode 属性も追加されました。嬉しいのか悲しいのかわかりません。したがって、quirks モードで実行されているかどうかを判断するコードは次のとおりです:
http://www.mangguo.org/x-ua-compatibility-ie8-compatibility-mode/
コードをコピーします コードは次のとおりです:

var isQuirk = (document.documentMode) ? (document.documentMode==5) ? :
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"); 0 ? 0 :
}、

非常に危険なアプローチであり、プロトタイプの実装よりも悪いため、他の部分を修正することはできません。これが、その UI のサイズが非常に大きい理由でもあります。また、側面からの優れた JQuery 実装方法も強調しています。ただし、JQuery の実装も非常に複雑です。この要素の正確な値を取得できない場合、この走査は多大なコストを消費します。また、Dean Edwards から素晴らしいハックを借用しました:
コードをコピーします コードは次のとおりです:

var ConvertPixelValue = function(el, value){
var style = el.style,left = style.left,rsLeft = el.runtimeStyle.left;
el.runtimeStyle.left = el.currentStyle.left; ;
style.left = 値 ||
var px = style.pixelLeft;
el.runtimeStyle.left = rsLeft; 🎜> }
//この関数は Dean Edwards によって提供されています。最も古いソースは以下のリンクにあります。
// http://erik.eae.net/archives/2007/07/27/18.54 .15/#comment-102291
//2 番目のパラメーターは、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 に置き換えて、ハックを有効にします。
el.runtimeStyle.left = el .currentStyle.left;
// 10em など、px 以外の単位値を style.left に代入します。
// 必ず style.pixelLeft を使用してください。それを取得するには、そうでないと値が不正確になります
//style.pixelLeft を使用すると 160 が取得され、style.left を使用すると 150px が取得されます
//すべてが変換されている場合、style.left は次のようになります不正確です。
var px = style.pixelLeft;
style.left = left;//データを復元
el.runtimeStyle.left = rsLeft;//データを復元
return
}


このハックは、em、pc、pt、cm、in、ex およびその他の単位を px に変換するために使用されます (もちろんパーセンテージは除きます)。
戻って getWidth 関数を見てください。主な問題は、padding-left と padding-right の値を取得することです。標準ブラウザでは、getComputedStyle を使用して、変換された正確な値を px 単位で簡単に取得できます。 IE では、値 2em を指定すると 2em が返されますが、これは遅延です。 「CSS の継承と Auto」の記事で、パディングは非継承プロパティであることも指摘しました。つまり、auto リストでは無意味な値の継承を扱う必要がなく、パディングはリストされていません。 auto のあいまいな値を扱う必要性が減ります。さらに、これは測定可能な単位であり、各ブラウザは親切にもデフォルト値を 0px に設定します。値の単位がpxではないのでpxに変換します。 Dean Edwards のハックをメイン関数 getStyle に統合するだけです。 padding-left がパーセンテージの場合、その親要素の幅を取得し、それにパーセンテージを掛けます。全体として、通過するレベルの数と計算の数は最小限に抑えられます。この点に関して、私のこの側面の処理は JQuery よりもはるかに優れています (JQuery には計算範囲にボーダーとマージンも含まれますが、IE ではボーダーとマージンの値があいまいで、JQuery が親の値を計算する必要があります)つまり、要素は親のプロパティを 5 回再計算する必要がありますが、私にとっては、奇妙なモードでは 1 回だけです。 Firefox の getComputedStyle よりも IE で取得した値の方が正確な場合があります(ただし、精度が高すぎるようなので、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 値 = (style == 'width') ['left', 'right'] : ['top' , 'bottom']、サイズ = 0;
if(isQuirk){
return el[camelize("offset-" style)]
}else{
var client = parseFloat(el[camelize ("client-" style)]),
paddingA = parseFloat(getStyle(el, "padding-" value[0])),
paddingB = parseFloat(getStyle(el, "padding -" 値[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"
}
}
戻り値;//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
コードをコピー コードは次のとおりです:

//境界線を細い中太に変換します
if (/^ (border).(width)$/.test(style)){
var s = style.replace("width","style"),
b = {
thin:[ "1px" ,"2px"],
medium:["3px","4px"],
thick:["5px","6px"]
}; = "medium " && getStyle(el,s) == "none"){
return "0px";
}
return !!window.XDomainRequest b[value][0] : b[ value][ 1];
}

上、左、右、下を見ると、Firefox と safari が遅延して auto を返すとは予想していませんでした。正直に大切にします。 Microsoft の便利な機能がすべてのブラウザですでにサポートされているため、解決策も非常に簡単です。

コードをコピー コードは次のとおりです。
//top|left|right| を変換します。ボトムの auto
if(/(top|left|right|bottom)/.test(style) && value == "auto"){
return el.getBoundingClientRect()[style]
}

さて、記事が長くなってしまいましたので、続きは次回にお話します。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。