px単位の値を取得する(その2)
JavaScript の勉強を始めた頃(去年の今頃)こういう日記を書いてました。
IEで width: "3em", width: "auto" から px単位の値を取得する - latest log
uuStyle.toPixel() を、よりクロスブラウザなコードにしてみました。
<script> var _ie = document.uniqueID; var _webkit = navigator.userAgent.indexOf("WebKit") > 0; var _int = parseInt; var _runstyle = _ie ? "currentStyle" : document.defaultView.getComputedStyle; var IMPORTANT = "important"; var POSITION = "position"; var ABSOLUTE = "absolute"; var DISPLAY = "display"; var BLOCK = "block"; var LEFT = "left"; var uuStyle = { // uuStyle.toPixel - covert unit // toPixel(node, 123) -> 123 // toPixel(node, "12px") -> 12 // toPixel(node, "12pt") -> 16 // toPixel(node, "12em") -> 192 toPixel: function(elm, // @param Node: context value) { // @param String/Number: // @return Number: pixel value if (typeof value === "string") { var st = elm.style, mem1 = st[LEFT], rs, mem2, mem3; if (_ie) { rs = elm.runtimeStyle, mem2 = rs[LEFT]; // keep !important value // overwrite rs[LEFT] = elm[_runstyle][LEFT]; st[LEFT] = value; // get pixel value = st.pixelLeft; // restore st[LEFT] = mem1; rs[LEFT] = mem2; } else { // overwrite if (_webkit) { mem2 = st.getPropertyValue(POSITION); mem3 = st.getPropertyValue(DISPLAY); st.setProperty(POSITION, ABSOLUTE, IMPORTANT); st.setProperty(DISPLAY, BLOCK, IMPORTANT); } st.setProperty(LEFT, value, IMPORTANT); // get pixel value = _int(_runstyle(elm, "")[LEFT]); // restore st.removeProperty(LEFT); st.setProperty(LEFT, mem1, ""); if (_webkit) { st.removeProperty(POSITION); st.removeProperty(DISPLAY); st.setProperty(POSITION, mem2, ""); st.setProperty(DISPLAY, mem3, ""); } } } return value || 0; }, // uuStyle.getPixel - get pixel value // getPixel(node, "left") // getPixel(node, "width") getPixel: function(elm, // @param Node: prop) { // @param String: style property name // @return Number: pixel value function dim(horizontal) { var r = elm.getBoundingClientRect(); return horizontal ? (r.right - r.left) : (r.bottom - r.top); } var rv; if (_ie) { switch (prop) { case "width": return elm.clientWidth || dim(1); case "height": return elm.clientHeight || dim(0); } rv = elm[_runstyle][prop]; (rv === "auto") && (rv = uuStyle.toPixel(elm, rv)); } else { rv = _runstyle(elm, "")[prop]; } return _int(rv) || 0; } }; </script>
説明
uuStyle.toPixel(elm, value) は、値(value)が ある要素(elm)で 何px なのかを返します。端数は切り捨てます。
// 20pt が body要素で 何px に相当するのか調べる alert(uuStyle.toPixel(document.body, "20pt")); // windows なら 27 または 26
uuStyle.getPixel(elm, prop) は、スタイル(prop)の計算済みの値が 何px なのかを返します。prop には、"left", "top", "width", "fontSize" など、長さに関係するプロパティ名を指定します。width: "auto" が指定されている場合でも適切な値を取得できます。
alert(uuStyle.getPixel(document.body, "fontSize")); // windows なら 16
処理を読み解くヒントをいくつか
- st.setProperty(prop, value, "important") は !important を js レベルで設定する方法の一つです。
- !important を確実に取り去るには、removeProperty(prop) します。
- st.setProperty(prop, value, null) だとうまくいきません。
- !important を確実に取り去るには、removeProperty(prop) します。
- WebKit(Safari, Google Chrome) は、ブロックレベル + 絶対配置要素以外では left の値がゼロになるため、強制的に display: block + position: absolute の状態にして、left 取得後に戻しています。
- uuStyle.getPixel(インライン要素, "width") は、WebKit, Gecko でゼロになりますが、IE や Opera では予想外の値が返ります。これは "width" 以外にも "height", "top", "right", "bottom", "right" などでも同じです。
テストコード
<!doctype html><html><head><title></title> <script> var _ie = document.uniqueID; var _webkit = navigator.userAgent.indexOf("WebKit") > 0; var _int = parseInt; var _runstyle = _ie ? "currentStyle" : document.defaultView.getComputedStyle; var IMPORTANT = "important"; var POSITION = "position"; var ABSOLUTE = "absolute"; var DISPLAY = "display"; var BLOCK = "block"; var LEFT = "left"; var uuStyle = { // uuStyle.toPixel - covert unit // toPixel(node, 123) -> 123 // toPixel(node, "12px") -> 12 // toPixel(node, "12pt") -> 15.996 // toPixel(node, "12em") -> 12em * 1 toPixel: function(elm, // @param Node: context value) { // @param String/Number: // @return Number: pixel value if (typeof value === "string") { var st = elm.style, mem1 = st[LEFT], rs, mem2, mem3; if (_ie) { rs = elm.runtimeStyle, mem2 = rs[LEFT]; // keep !important value // overwrite rs[LEFT] = elm[_runstyle][LEFT]; st[LEFT] = value; // get pixel value = st.pixelLeft; // restore st[LEFT] = mem1; rs[LEFT] = mem2; } else { // overwrite if (_webkit) { mem2 = st.getPropertyValue(POSITION); mem3 = st.getPropertyValue(DISPLAY); st.setProperty(POSITION, ABSOLUTE, IMPORTANT); st.setProperty(DISPLAY, BLOCK, IMPORTANT); } st.setProperty(LEFT, value, IMPORTANT); // get pixel value = _int(_runstyle(elm, "")[LEFT]); // restore st.removeProperty(LEFT); st.setProperty(LEFT, mem1, ""); if (_webkit) { st.removeProperty(POSITION); st.removeProperty(DISPLAY); st.setProperty(POSITION, mem2, ""); st.setProperty(DISPLAY, mem3, ""); } } } return value || 0; }, // uuStyle.getPixel - get pixel value // getPixel(node, "left") // getPixel(node, "width") getPixel: function(elm, // @param Node: prop) { // @param String: style property name // @return Number: pixel value function dim(horizontal) { var r = elm.getBoundingClientRect(); return horizontal ? (r.right - r.left) : (r.bottom - r.top); } var rv; if (_ie) { switch (prop) { case "width": return elm.clientWidth || dim(1); case "height": return elm.clientHeight || dim(0); } rv = elm[_runstyle][prop]; (rv === "auto") && (rv = uuStyle.toPixel(elm, rv)); } else { rv = _runstyle(elm, "")[prop]; } return _int(rv) || 0; } }; </script> <style> p { margin: 0; padding: 0 } div { position: absolute; background-color: #eef; opacity: 0.8 } .tgt0 { border: 3px dotted skyblue } .tgt1 { border: 3px solid red } .tgt2 { border: 3px solid green } .tgt3 { border: 3px solid blue } .tgt4 { border: 3px solid pink } .tgt5 { border: 3px solid tomato } .tgt6 { border: 3px solid yellowgreen } .tgt7 { border: 3px solid darkcyan } #out { z-index: 4; top: 50px; } </style> <script> function calcStyle() { var body = document.body; var tgt0 = document.getElementById("tgt0"); var tgt1 = document.getElementById("tgt1"); var tgt2 = document.getElementById("tgt2"); var tgt3 = document.getElementById("tgt3"); var tgt4 = document.getElementById("tgt4"); var tgt5 = document.getElementById("tgt5"); var tgt6 = document.getElementById("tgt6"); var tgt7 = document.getElementById("tgt7"); var rv = []; var fn1 = uuStyle.toPixel; var fn2 = uuStyle.getPixel; rv.push("<b>toPixel</b>"); rv.push('<p class="tgt0">', ["12px", fn1(tgt0, "12px")].join(" -> "), "</p>"); rv.push('<p class="tgt0">', ["12pt", fn1(tgt0, "12pt")].join(" -> "), "</p>"); rv.push('<p class="tgt0">', ["12em", fn1(tgt0, "12em")].join(" -> "), "</p>"); rv.push("<b>getPixel</b>"); rv.push('<p class="tgt1">', ["left:20px", fn2(tgt1, "left")].join(" -> "), "</p>"); rv.push('<p class="tgt2">', ["left:20em", fn2(tgt2, "left")].join(" -> "), "</p>"); rv.push('<p class="tgt3">', ["left:20pt", fn2(tgt3, "left")].join(" -> "), "</p>"); rv.push('<p class="tgt4">', ["left:auto", fn2(tgt4, "left")].join(" -> "), "</p>"); rv.push('<p class="tgt1">', ["width:auto", fn2(tgt1, "width")].join(" -> "), "</p>"); rv.push('<p class="tgt2">', ["width:auto", fn2(tgt2, "width")].join(" -> "), "</p>"); rv.push('<p class="tgt3">', ["width:auto", fn2(tgt3, "width")].join(" -> "), "</p>"); rv.push('<p class="tgt4">', ["width:auto", fn2(tgt4, "width")].join(" -> "), "</p>"); rv.push('<p class="tgt5">', ["width:50%", fn2(tgt5, "width")].join(" -> "), "</p>"); rv.push('<p class="tgt5">', ["height:50%", fn2(tgt5, "height")].join(" -> "), "</p>"); rv.push('<p class="tgt6">', ["width:20em", fn2(tgt6, "width")].join(" -> "), "</p>"); rv.push('<p class="tgt6">', ["height:20em", fn2(tgt6, "height")].join(" -> "), "</p>"); rv.push('<p class="tgt7">', ["width:auto", fn2(tgt7, "width")].join(" -> "), "</p>"); rv.push('<p class="tgt7">', ["height:auto", fn2(tgt7, "height")].join(" -> "), "</p>"); rv.push(["20pt", fn1(document.body, "20pt")].join(" -> ")); alert(uuStyle.getPixel(document.body, "fontSize")); document.getElementById("out").innerHTML = rv.join("<br />"); } </script> </head> <body> <div> <input type="button" value="calc" onclick="calcStyle()" /> </div> <div class="tgt0" id="tgt0" style="left: 200px"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </div> <div class="tgt1" id="tgt1" style="top: 200px; left: 20px"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </div> <div class="tgt2" id="tgt2" style="top: 250px; left: 20em"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </div> <div class="tgt3" id="tgt3" style="top: 300px; left: 20pt"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </div> <div class="tgt4" id="tgt4" style="top: 350px;"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </div> <div class="tgt5" id="tgt5" style="top: 400px; width: 50%; height: 50%"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </div> <div class="tgt6" id="tgt6" style="top: 450px; width: 20em; height: 20em"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </div> <p class="tgt7" id="tgt7"> tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt tgttgttgttgttgttgttgttgttgttgttgttgttgttgt </p> <div id="out"></div> </body></html>