JavaScript: テストプログラムの実行とソース表示
概略
JavaScript: HTML 要素にテキストを出力する に関連して。
ローカルでちょっと試す HTML ではなくて、
Web にサンプルコードを表示し実行するときの話です。
うちのページでもサンプルコードを表示して実行する HTML
がたくさんありますが、
このような HTML を作成するときの参考になりそうな話です。
document.write()
をなるべく使わない方が良いという話も書いてしまったので
document.write() を使わない方法でいろいろ。
JavaScript: document.write()
まとめ
- TestJS_codetest_currentScript01.html
1行のプログラムテキストを eval() で実行して結果を表示する例。 結果は pre タグに Element.append() で出力する。 - TestJS_RegExp_replace01.html
実際のテストサンプルの例。
実行結果を pre タグに出力する
document.write() を使わずに Element.append() で HTML にテキストを出力します。
- TestJS_codetest_append01.html
テストプログラムの実行結果を pre タグに Element.append() で出力するサンプル。
JavaScript: プログラムを実行するタイミング
実行結果の文字列を整形する
出力文字の列が揃うように String.padStart(), String.padEnd() などで調整します。
JavaScript: 文字列の処理 (1)
- TestJS_codetest_append02.html
String.padEnd() で出力する文字列の幅を調整する例。
テストコードを eval() 関数で実行する (2023/12)
1行程度のコードと結果を出力する場合は eval() 関数を使うと便利です。
JavaScript: eval 関数
下のサンプルコードでは value.toFixed() が 2回現れています。 1個目はコード表示用の単なる文字列で、2個目の ${ } の中は実際に処理されるプログラムコードです。
writeln(`value.toFixed() ${ value.toFixed() } 引数省略は 0 と同じ`);
同じ文字列を 2回書くと修正ミスなどで 表示するコードと実際に実行するコードが異なるといったトラブルが起こりやすいので、 両者を共通にして 1つの文字列だけで済むようにします。
let s = "value.toFixed()" writeln(`${s} ${ eval(s) } 引数省略は 0 と同じ`);
- TestJS_codetest_eval01.html
eval() 関数で関数の動作テストを行うサンプル。 コードのテキストと実行結果を左右に並べて表示します。 1行程度の小さなコードに適しています。
上のサンプルを簡素化する (2023/12)
上の TestJS_codetest_eval01.html では test() を外部の function にしているので引数 value も必要になります。 value がないと変数のスコープの外側になるので value が見つからないエラーが出ます。
//TestJS_codetest_eval01.html では test() に引数 value が必要なのが難点。 function test(code, comment = "", value = null){ ... } //テスト本体 { const value = 123.15; // value も引数で渡すことが必要。 test("value.toFixed()", "引数省略は 0 と同じ", value); ... }
毎回、引数 value を書くのも面倒なのでもう少し簡素化してみます。 test() をアロー関数で本体の処理の変数にします。 このようにすると value は同じスコープ内の変数になり test() 内でも見えている変数になるので、test() で引数 value を渡す必要がなくなります。
//テスト本体 { //test() をアロー関数の変数にする。引数 value は不要になります。 const test = (code, comment = "") => { try { writeResult(`${ code }`, `${ eval(code) }`, comment); } catch(e){ writeResult(code, "(error)", `${comment} ${e}`); } }; const value = 123.15; test("value.toFixed()", "引数省略は 0 と同じ"); //引数 value は不要になります。 ...
- TestJS_codetest_eval02.html
TestJS_codetest_eval01.html を簡素化したサンプル。
セキュリティに注意
eval() 関数はセキュリティトラブルの話が出てくるので 固定の文字列ではない文字列 (ユーザーが入力した任意のコードなど) を処理するときには注意が必要です。
pre 要素内に script 要素を置く
pre 要素内に script 要素を置くと pre に id を持たせる必要がなくなります。 1つの HTML ファイル内に複数のサンプルを並べるときに便利です。
現在実行中のスクリプトがある script 要素は document.currentScript で取り出せます。 parentNode で親のノードをたどると script タグの外側にある親要素 (ここでは pre) も取り出せます。
- MDN: Document.currentScript
(補足) document.currentScript は IE で使用できませんでした。
//script タグの親要素の pre に append() で文字列を出力する。 function writeln(s){ document.currentScript.parentNode.append(s + "\n"); } //上の処理を分かりやすくすると? function writeln(s){ //現在実行中の script 要素を取得する。 const script = document.currentScript; //script 要素の親の pre 要素を取得する。 const pre = script.parentNode; // pre に文字列 s を出力する。 pre.append(s + "\n"); } // (参考) querySelector() では id 名が分からないと親の pre を // 取り出せないので pre に id 名を付ける必要があります。 function writeln(s){ document.querySelector( id名 ).append(s + "\n"); }
- TestJS_codetest_currentScript01.html
pre 内に script を置くサンプル。document.currentScript で pre を取り出して文字列を出力する。
テストでエラーが発生しない場合は try catch も不要です。 下は正規表現のテストプログラムの例です。
- TestJS_RegExp_replace01.html
JavaScript: 文字列の処理、正規表現 (1)<pre> <script> { // testEval() の実行をアロー関数で簡素化する。 // サンプルコード内の変数 s は同じスコープにあるのでアロー関数の引数は不要です。 // try catch も省略。 const test = (code, comment = "") => writeResult(`${ code }`, `${ eval(code) }`, comment); const s = "_abc_ABC_ABC_Abc_ABC_"; writeln("s =" + s); writeResult("code", "result", "comment"); test('s.replace(/ABC/, "Goro")', "オプションなしの場合は最初に見つかった ABC の 1個だけを置換します。"); test('s.replace(/ABC/g, "Goro")', "g オプションで ABC をすべて置換します。"); } function writeResult(code, result, comment){ writeln(`${code.padEnd(28)} ${result.padEnd(28)} ${comment}`); } function writeln(s){ document.currentScript.parentNode.append(s + "\n"); } </script> </pre>
- JavaScript: 文法などのノート
実際のテストの例。
サンプルのソースコードを表示する
ソースコードも HTML に表示します。こちらも Web 表示用のコードと実際に実行するコードを別々に用意すると面倒なので、 実行するコードをそのままテキスト化して表示します。
ソースコードの表示は HTML 読み込み完了後 (onload イベント)
のタイミングでコードテキストを取り出してソース表示用の要素にセットします。
JavaScript: プログラムを実行するタイミング
HTML 読み込み完了後に実行するサンプルの場合
先に HTML の読み込みの途中では何も行わず、 読み込みを完了した後でボタンを押して何か処理を行うサンプルの例です。
- TestJS_codetest_source01.html
サンプルプログラムのソースコードを表示する。
HTML を読み込む途中で実行するサンプルの場合
HTML を読み込む途中ですぐに実行するプログラムでは 実行結果の文字列も HTML の中に出力されるので、 ソースコードの表示のときに実行結果を除去したテキストにすることが必要です。
上の TestJS_codetest_currentScript01.html などのように pre タグ内に script タグを置いて pre.append() で文字列を出力するプログラムでは </script> の後に append() で出力した文字列が追加されます。
<pre> <script> writeln("サンプルテキスト"); function writeln(s){ document.currentScript.parentNode.append(s + "\n"); } </script> <!--ここに writeln() の文字列が出力される--> </pre>
- 問題ありの例
TestJS_codetest_source02error.html
HTML の読み込み途中で pre.append() や document.write() するサンプル。 コードを表示するとソースコードの中に実行結果の文字列も混ざります。
単純なサンプルならコードテキストの </script> と </pre> の間のテキストを削除すれば間に合います。
// /script タグと /pre タグの間のテキストを除去する例。 text = text.replace(/<\/script>[\s\S]*?<\/pre>/g, "</" + "script>\n</pre>");
ただし、 script タグや pre タグを複数含むコードになるとまずいです。 下の例のようなコードの場合 </script> から </pre> までの間のテキストを削除すると必要なテキストまで削除されます。
◎ </script> と </pre> の間のテキストを単純に削除するとまずい例 ・script タグが複数あるサンプル <pre> <script src="sample.js"></script> <!--削除されるテキスト--> <script> writeln("サンプル"); </script> </pre>
対策例として実行結果が出力される箇所に目印になる文字列 ( 例えば、<!--result--> ) を入れておいて、 直前に現れる </script> タグから <!--result--> までの間のテキストを削除します。
◎ 実行結果のテキストを削除 <pre> <script src="sample.js"></script> <script> writeln("サンプル"); </script> ここに実行結果が出力される。このテキストを削除する。 <!--result--> </pre>
- TestJS_codetest_source03.html
HTML の読み込み途中でテストコードを実行し、ソースも表示するサンプル。
CSS や共通の JavaScript は外部ファイルに分離する。
1つの HTML ファイルにサンプルを複数並べる場合は <div id="SAMPLE"> の id が 1個だけだとまずいので id にも通し番号を付けるなどの工夫をします。
行番号を表示する
pre タグで HTML のソースコードを表示するサンプル。 こちらは見た目のイメージです。 行番号とソースコードの pre タグを左右に並べます。
CSS: flex