* JavaScript メモ [#e3baac80] JavaScript に関するメモを追記していく。 #contents ** IEとFirefoxの違い [#x07c8e64] Firefoxの方が[[W3C勧告:http://www.asahi-net.or.jp/~SD5A-UCD/rec-html401j/cover.html]]に忠実なようだけど、IEのシェアもいまだに大きい以上は無視できない(事実上標準)。 - [[HTML 4.01仕様書:http://www.asahi-net.or.jp/~SD5A-UCD/rec-html401j/cover.html]] - [[W3Cの仕様書等の文書の日本語訳集:http://www.w3.org/Consortium/Translation/Japanese]] - [[Gecko - Wikipedia:http://ja.wikipedia.org/wiki/Gecko]] - [[Trident - Wikipedia:http://ja.wikipedia.org/wiki/Trident_(%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3)]] *** DOM扱いの相違 [#yc1faec2] 相違点を追記していく。 - タグで囲まれる文字列(CDATA)を取得するには、Firefoxでは textContent、IEではinnerText。(nodeValue ならどちらでもOK) - childNode のとり方が違う。Firefoxは空白ノードをカウントする。IEはカウントしない。&br; 参考-> [[DOM入門 子ノードの取得:http://www.ajaxtower.jp/dom/ini/index4.html]] *** Script実装の相違 [#vb02ebae] 相違点を追記していく。 - inputタグなどの onchange イベントのタイミングが違うっぽい。&br; これが気になる場合は onclick などで代替。 - IEのonclickなどでsubmitする場合は、最後に return false をしないと、ソケットエラー(Firefoxなら書かなくても大丈夫)。 - IEではタグに勝手にいろんな属性を追加できる(divタグにvalueをつけたりとか)が、Firefoxではそれを取得できないことがある。&br; 確実に拡張属性をとるなら、getAttribute()で。 - イベントを追加するには、IEはattachEvent()、FirefoxはaddEventListener()。 *** 参考 [#p96b97ab] - [[一撃必殺JavaScript日本語リファレンス:http://www.openspc2.org/JavaScript/ref/]] - [[Gecko DOM Reference:http://developer.mozilla.org/ja/docs/Gecko_DOM_Reference]] - [[とほほのWWW入門:http://www.tohoho-web.com/]] -- [[WWW入門:http://www.tohoho-web.com/www.htm]] - [[JavaScriptist:http://javascriptist.net/]] ** IEでJavaScriptをデバッグ [#f3e1ab7a] *** ブラウザの設定を有効にする [#o4253836] IEの「インターネットオプション」->「詳細設定」タブを開いてその中の「ブラウズ」カテゴリから「スクリプトのデバッグを使用しない(Internet Explorer)」というチェックがあるので、このチェックを''外す''(デフォルトでチェックがONになっている)。 ただ、これは Microsoft Office がインストールされている必要があるかも? *** Windows Script Debugger を使う [#c9c79368] [[Windows Script Debugger:http://www.microsoft.com/downloads/details.aspx?displaylang=ja&FamilyID=e606e71f-ba7f-471e-a57d-f2216d81ec3d]] を使う。 - [[Windows Script Debugger:http://www.microsoft.com/downloads/details.aspx?displaylang=ja&FamilyID=e606e71f-ba7f-471e-a57d-f2216d81ec3d]] *** Internet Explorer Developer Toolbar を使う。 [#o1d90c12] 最近(といっても2007年5月)出たフリーのデバッグツール。IE6と7に対応している。 Firebug のような感じで使えるので、WSD より使いやすいかも。 ただ、ScriptやDOM、CSSなどの値をインスペクトすることはできるが、 Firebugのように変更して確認することはできないっぽい? - [[Internet Explorer Developer Toolbar:http://www.microsoft.com/downloads/details.aspx?FamilyID=e59c3964-672d-4511-bb3e-2d5e1db91038&displaylang=en]]&br; 「Download」をポチッと押して、あとはどんどんNext、終わったらIE再起動でインストール完了。 ** FirefoxでJavaScriptをデバッグ [#mbb0848f] *** Firebug を使う(お勧め) [#obb0b3ec] これはかなり使いやすい。まずは下記ページからアドオンをインスコ。 - [[Firebug:https://addons.mozilla.org/ja/firefox/addon/1843]] 「署名がありません」とかいわれるけど気にしない。 インストールされたらFirefoxを再起動。メニューの「ツール」に「Firebug」という項目が増えているのでここから「Open Firebug」を選択、「Enable Firebug」をクリックするとデバッグが開始される。ブラウザで動作を見ながらステップ実行できるので、非常にやりやすい。 *** JavaScript Debugger を使う [#z9083413] これはRADのような画面でScriptを追うツール。画面で確認する必要がなく、シンタックスや処理内容だけデバッグすれば良いときはこちらが良いのかも。 - [[JavaScript Debugger:https://addons.mozilla.org/ja/firefox/addon/216?id=216&vid=512]] ** サンプル [#l215c6fa] *** フォーカスが当たると文字が消える [#n99a79d3] focusinput.js #code(javascript){{ function removeText(formName,elementName){ var target = document.forms[formName].elements[elementName]; if(target.value == target.defaultValue){ target.value = ""; target.style.color='#000000'; } } function insertText(formName,elementName){ var target = document.forms[formName].elements[elementName]; if(target.value == target.defaultValue || target.value == ""){ target.value = target.defaultValue; target.style.color='#999999'; } } }} sample.html #code(html){{ <html> <head> <title>sample</title> <script type="text/javascript" src="focusinput.js"></script> </head> <body> <form name="form1"> <input type="text" size="50" onfocus="removeText(this.form.name,this.name);" onblur="insertText(this.form.name,this.name);" maxlength="256" value="入力してください" style="color: #999999;"> </form> </body> </html> }} *** submit でボタンを無効にする [#u116a839] [[こちら:http://espion.just-size.jp/archives/05/220233057.html]]のを参考に、再表示で元に戻す処理も追加してみた。 #code(javascript){{ var DisableSubmit = { init: function() { this.addEvent(window, 'load', this.onLoad()); }, onLoad: function() { var self = this; return function () { for (var i = 0; i < document.forms.length; ++i) { var elements = document.forms[i].getElementsByTagName('input'); if (!elements || elements.length == 0) { continue; } self.setDisable(elements, false); self.addEvent( document.forms[i], 'submit', self.onSetDisable(elements, true) ); } } }, setDisable: function(elements, flag) { for (var i = 0, element; element = elements[i]; i++) { if (element.type == 'submit') { element.disabled = flag; } } }, onSetDisable: function(elements, flag) { var self = this; return function () { self.setDisable(elements, flag); // see: http://support.microsoft.com/kb/890981/ja if (isIE && elements && elements.length > 0) { for (var j = 0; j < elements.length; j++) { if (elements[j].type.toLowerCase() == 'file') { self.onCancel(); break; } } } } }, onCancel: function() { var self = this; window.setTimeout(function() { for (var i = 0; i < document.forms.length; ++i) { var elements = document.forms[i].getElementsByTagName('input'); if (!elements || elements.length == 0) { continue; } self.setDisable(elements, false); } }, 1000); }, addEvent: function(element, type, event) { if(element.addEventListener) { element.addEventListener(type, event, false); } else if(element.attachEvent) { element.attachEvent('on'+type, event); } else { element['on'+type] = event; } }, removeEvent: function(element, type, event) { if(element.removeEventListener) { element.removeEventListener(type, event, false); } else if(element.detachEvent) { element.detachEvent('on'+type, event); } else { element['on'+type] = event; } } } DisableSubmit.init(); }} ただ、このままだと、submit後にページ遷移しないケース(問い合わせダイアログを開いてキャンセルした場合など)で ボタンが無効なままなので、onCancel() で元に戻すようにする。 #code(javascript){{ function confirm_msg(msg) { if(window.confirm(msg)){ return true; } if(DisableSubmit){ DisableSubmit.onCancel(); } return false; } }} *** 選択文字列をタグで括る [#hc3fed74] 選択文字列の取得の仕方がFirefoxとIEとでちょっと違うので、やむを得ず判定を使う。 taginsert.js #code(javascript){{ var isIE = (navigator.appName.toLowerCase().indexOf('internet explorer')+1?1:0); function getAreaRange(obj) { var pos = new Object(); if (isIE) { obj.focus(); var range = document.selection.createRange(); var clone = range.duplicate(); clone.moveToElementText(obj); clone.setEndPoint( 'EndToEnd', range ); pos.start = clone.text.length - range.text.length; pos.end = clone.text.length - range.text.length + range.text.length; } else if(window.getSelection()) { pos.start = obj.selectionStart; pos.end = obj.selectionEnd; } return pos; } function insertTag(textAreaId,tag_st,tag_ed) { //var target = document.forms[].elements[]; //var target = window.opener.document.getElementById(textAreaName); var target = document.getElementById(textAreaId); if (target == null) return; var pos = getAreaRange(target); var val = target.value; var range = val.slice(pos.start, pos.end); var beforeNode = val.slice(0, pos.start); var afterNode = val.slice(pos.end); var insertNode; if (range || pos.start != pos.end) { insertNode = '<' + tag_st + '>' + range + '</' + tag_ed + '>'; target.value = beforeNode + insertNode + afterNode; } else if (pos.start == pos.end) { insertNode = '<' + tag_st + '>' + '</' + tag_ed + '>'; target.value = beforeNode + insertNode + afterNode; } } function insertAnchor(textAreaId,url,target) { var tag_st = "a href=\"" + url + "\""; if (target != null && target != "") tag_st = tag_st + " target=\"" + target + "\""; var tag_ed = "a"; insertTag(textAreaId, tag_st, tag_ed); } }} 以下は、idが"edit"というテキストエリアにタグを挿入する例。 #code(html){{ <script type="text/javascript"> function insertTest() { var url = document.frm.url.value; var tar = document.frm.target.value; insertAnchor('edit', url, tar); } </script> <body> <form name="frm"> <textarea id="edit"></textarea><br> <input type="text" name="url" size="50"> <input type="text" name="target" size="12"><br> <input type="button" value="挿入" onclick="insertTest()"> </form> </body> }} *** 入力フィールドをクリアする [#mb58196d] form の入力フィールド (input) を全て空白にするスクリプト。 そんなもん reset で良くね?と思ったアナタは罠にハマりますぜ。 reset というのはクリアするのでなく「初期値」に戻すのです。 なので、もし何かデフォルト値 (defaultValue) があったら、その値が再入力されるので、 reset = 真っ白にする、ということではないのです。 以下そのスクリプト。 #code(javascript){{ function clearFilelds() { for (var i = 0; i < document.forms.length; i++) { var fields = document.forms[i].getElementsByTagName('input'); if (fields && fields.length > 0) { for(var j = 0; j < fields.length; j++) { if (fields[j].type.toLowerCase() == "text") { fields[j].value = ""; } } } var textareas = document.forms[i].getElementsByTagName('textarea'); if (textareas && textareas.length > 0) { for(var j = 0; j < textareas.length; j++) { textareas[j].value = ""; } } } } }} *** submit 時にタグを decode [#b03f6cb2] 逆(submit 時にエスケープ)はよくあるけど、これはあんまりない。 というか使う機会もあまりないんですが。 submit するときに、エスケープされているタグを元に戻す処理。 WYSIWYG エディタを使ってるシーンなどで欲しくなることがあります。 以下、その処理だけど、途中 IE が分岐してあるのは、 というのは半角スペースを表現するコードなんだけど、 普通これは 20h に解釈されるべきなのに、 IE ではなぜか A0h で出力している為。これも罠だよね。 #code(javascript){{ var isIE = (navigator.appName.toLowerCase().indexOf('internet explorer')+1?1:0); function appendEvent(element, type, event) { if(element.addEventListener) { element.addEventListener(type, event, false); } else if(element.attachEvent) { element.attachEvent('on'+type, event); } else { element['on'+type] = event; } } function encodeHTML(val) { if (!val) return val; var dst = val; dst = dst.replace(/&/ig, "&"); dst = dst.replace(/</ig, "<"); dst = dst.replace(/>/ig, ">"); //dst = dst.replace(/"/ig, """); dst = dst.replace(/ /ig, " "); return dst; } function decodeHTML(val) { if (!val) return val; var dst = val; dst.replace(/ /ig, " "); dst.replace(/"/ig, "\""); dst.replace(/>/ig, ">"); dst.replace(/</ig, "<"); dst.replace(/&/ig, "&"); return dst; } function decodeHTMLForIE(val) { if (!val) return ; var dst = ""; for ( var i = 0; i < val.length; i++ ) { var c = escape( val.charAt(i) ); switch ( c ) { case '%26': dst = dst + "&"; break;//& case '%3C': dst = dst + "<"; break;//< case '%3E': dst = dst + ">"; break;//> case '%A0': dst = dst + " "; break;// (%20) default : dst = dst + val.charAt(i); break; } } return dst; } function convertDatas() { var elements = document.getElementsByTagName('input'); if (!elements) return; for (var i = 0; i < elements.length; i++) { if (elements[i].type.toLowerCase() == 'text' && elements[i].value) { if (isIE) { elements[i].value = decodeHTMLForIE(elements[i].value); } else { elements[i].value = decodeHTML(elements[i].value); } } } } function getSubmitFunction() { return function() { convertDatas(); } } function initForms() { var forms = document.getElementsByTagName('form'); if (!forms) return; for (var i = 0; i < forms.length; i++) { appendEvent(forms[i], 'submit', getSubmitFunction()); } } function getInitFunction() { return function() { //initForms(); convertDatas(); } } appendEvent(window, 'load', getInitFunction()); }} *** 改行挿入 [#vc4993fc] 普通の textarea なら Enter キーを押せば改行コードが挿入される。 それをボタンなりイベントなりでやるとこういうことになる。 #code(javascript){{ function insertReturn(target, code) { if( code != 13 ) return; var val = target.value; var returnCode = "\n"; if (isIE) { var sel = document.selection.createRange(); sel.text = returnCode; sel.select(); } else { var top = target.scrollTop; var pos = getAreaRange(target); var range = val.slice(pos.start, pos.end); var beforeStr = val.slice(0, pos.start); var afterStr = val.slice(pos.end); target.value = beforeStr + returnCode + afterStr; target.scrollTop = top; target.setSelectionRange(pos.start+1, pos.start+1); } target.focus(); } }} *** 数値チェック [#ne4edb48] isNaN()の逆。じゃあ !isNaN() で良いじゃないかと。 ブラウザや環境によってこのビルトイン関数が使えないみたいなので、正規表現で代替してみた。 #code(javascript){{ function isNum(str) { if (!str) { return false; } if ( str.match(/^[+-]?[\d]+$/) || str.match(/^[+-]?[\d]+\.[\d]+$/) ) { return true; } return false; } }} *** スクリプトで画像の上下を反転させる [#ubf60610] そんな無理しなくても、上下逆の画像をペイントか何かでつくればいいじゃん。 とか思ったら負け。 IE では filter が使えるので、FlipV で反転させる。 Firefox などのモダンブラウザはイメージオブジェクトが使えるので、それで。 canvas というタグもあるらしいけど、まだ対応してるブラウザが少なそう? flipv.js #code(javascript){{ function flipV(id) { var canvas = document.getElementById(id); if (!canvas) { return ; } var imgSrc = canvas.getAttribute('src'); if (!imgSrc) { return ; } if (navigator.appName.toLowerCase().indexOf('internet explorer')+1?1:0) { var imgNode = document.createElement('img'); imgNode.setAttribute('src', imgSrc); //imgNode.style.filter = 'FlipH();'; imgNode.style.filter = 'FlipV();'; canvas.appendChild(imgNode); } else { var imgNode = document.createElement('canvas'); var dc = imgNode.getContext('2d'); var img = new Image(); img.src = imgSrc; dc.rotate(Math.PI); dc.translate( -1 * img.width, -1 * img.height); dc.drawImage(img, 0, 0); canvas.appendChild(imgNode); } } }} flipv.html #code(html){{ <html> <head> <title>flipV</title> <script type="text/javascript" src="flipv.js"></script> </head> <body onload="flipV('canvas')"> <div id="canvas" src="sample1.jpg"></div> </body> </html> }} ----- [[MLEXP. Wiki]]