WebAssembly、Webの新しい時代へ
WebAssemblyは、この問題を解決することを目的としています。この記事では、開発者がWebAssemblyを総合的に理解できるように、WebAssemblyの起源から開発手法までを解説しています。
起源
まず、ブラウザ戦争から始めよう。マイクロソフトは、WindowsにInternet Explorerをバンドルするという固有の優位性でネットスケープを潰した後、数年にわたる沈黙の期間に入りました。一方、Netscapeは1998年にCommunicatorをオープンソース化し、Mozilla FoundationはFirefoxブラウザを派生させ、2004年にバージョン1.0をリリースした。これ以降、第二次ブラウザ戦争が始まった。2008年、GoogleがChromeを発表し、Firefoxの市場を徐々に侵食していっただけでなく、老朽化したInternet Explorerも抑えた。この戦いの後、2012年のStatCounterのデータでは、Chromeが僅差でInternet Explorerを抜き、世界で最も人気のあるブラウザとなった。
Google ChromeがInternet Explorerに勝利した理由を分析すると、Web標準へのフレンドリーな対応に加えて、優れたパフォーマンスが大きな要因であり、ブラウザのパフォーマンス競争の本質はJavaScriptエンジンにある。これまでのJavaScriptエンジンは、構文木をバイトコードインタプリタまでトラバースし、各ソースコードを対応するマシンコードに翻訳し、翻訳したマシンコードを保存せずに実行するという原始的な方法で実装されていたため、解釈が遅くなっていたのである。V8は、ウェブブラウザにおけるJavaScriptの実行性能を向上させるために、JavaScriptコードを実行時に効率的な機械語にコンパイルし、JIT(Just-In-Time)技術によって次に同じスニペットを実行するときに再びコンパイルせずに保存することで、数十倍の性能向上を獲得できるようにしたものである。
しかし、JavaScriptはuntyped(型付けされていない、変数に型がない)言語であるため、c=a+bという式が複数の意味を持つことに直結してしまう:。
- a と b が数値の場合、算術演算子 + はその値の合計を表します。
- a と b が文字列の場合、+ 演算子は文字列の連結を表します。
- ...
式が実行されるとき、JITコンパイラはaとbの型をチェックして、操作の動作を決定する必要があります。aとbがともに数値の場合、JITコンパイラはaとbを整数と識別し、変数が文字列になると、JITコンパイラは以前にコンパイルした機械語をプッシュバックする必要があるのです。このように、JavaScriptの型付けなし機能は、大きな性能コストの上に成り立っています。JIT コンパイラが変数の型変更に最適化するとはいえ、for-of, try-catch, try-finally, with 文や複合 let, const 代入を含む関数など、JavaScript エンジンが最適化しない、あるいはできないケースも多く存在します。
つまり、JavaScriptの型付け解除がJavaScriptエンジンの性能ボトルネックの一つであり、改善のためには、新たに強型付け言語を設計して開発者に型指定を強制するか、既存のJavaScriptに変数型を追加するかの二択となります。
マイクロソフトが開発したTypeScriptは、第一の改善策である。これは、JavaScriptの機能を拡張し、型アノテーション、コンパイル時の型チェック、型推論、消去などの機能を持たせた言語である。TypeScriptの開発者は変数を宣言する際に型を指定することで、JavaScriptエンジンがこの強い型付けの言語をより速く弱い型にコンパイルできるようになる。
2つ目の選択肢を見てみましょう。
コード1は、2つの引数(a、b)を持つJavaScriptの関数を表しています。通常のJavaScriptのコードと異なるのは、a=a|0、b=b|0であり、戻り値の後にマークアップによるビット単位のOR演算が行われている点です。この利点は、JavaScriptエンジンが変数の値を強制的に整数型に変換することができることです。変数の型をマークアップに加えることで、JavaScriptエンジンのコンパイルを高速化することができる。
変数型を追加するとWebのパフォーマンスが向上するので、C/C++などの静的型付けされたコードをJavaScriptの命令のサブセットに変換する方法はないでしょうか?上のコードは、正確にはコード2のC言語からコンパイルされたJavaScriptのサブセットとしてのasm.jsです。
実は、ブラウザーを使ってC/C++のプログラムを実行できるプロジェクトは、1995年からNPAPI(Netscape Plugin API)などが開発されています。2013年に登場したasm.jsは、C/C++で書かれたコンピューターソフトウェアをウェブアプリケーションとして実行し、より良いパフォーマンスを維持するための中間プログラミング言語で、Mozilla Firefoxはバージョン22以降、asm.jsに特に最適化した最初のウェブブラウザとなっています。
Googleも同様に、ネイティブ・コードをウェブ側で実行するための取り組みを行っています。Google Native Client(NaCl)は、Intel x86、ARM、MIPSのサブセット用のマシンコードをサンドボックス上で直接実行させるサンドボックス技術を使っている。プラグインをインストールすることなく、ブラウザから直接ネイティブな実行コードを実行できるため、マシンコードの動作速度に近い速度でWebアプリケーションを実行することが可能です。一方、Google Portable Native Client(PNaCl)は若干のバリエーションがあり、C/C++のソースコードをx86やARMのコードではなくLLVMの中間バイトコードにコンパイルし、リンクと同様に最適化するフロントエンドコンパイラーがいくつかある(表1に示す)。
タイプサポートがあれば、2番目のオプションは1番目のオプションよりもはるかに大きな性能向上の可能性を持っています。
しかし、asm.jsも既存のPNaClソリューションも、他のブラウザではサポートされていない多くの欠点(例えば、asm.jsを生成するためにコンパイルしたCソースコードの1KBのサイズは480KB)やジレンマを抱えており、Chromium issue tracking codeの2016年10月のコメントは、Google Native Clientグループが停止されたことを示しています。
asm.jsもPNaClも、Webブラウザーのパフォーマンスとコードの再利用のためのソリューションとして普遍的に受け入れられてはいませんが、上の表の機能をすべて備えた、優れたクロスベンダーのソリューションはあるのでしょうか?
WebAssemblyはこの問題を解決することを目的としています。
新しい時代
WebAssembly(略称:Wasm)は、Webへのコンパイルに適した、ポータブルで、サイズとロード時間の効率的な新しいフォーマットです。これは、JavaScriptのパフォーマンス問題を解決することを目的とした、プラットフォームに依存しない新しいバイナリコード形式です。この新しいバイナリ形式は、JavaScriptよりもはるかに小さく、ブラウザのJavaScriptエンジンで直接ロードして実行できるため、JavaScriptからバイトコード、バイトコードから実行前のマシンコードへのコンパイルにかかるJIT(Just In Time)時間が短縮されます。低レベル言語として、開発者がテキスト形式でデバッグできるAST(Abstract Syntax Tree)を定義しています。
WebAssemblyは、既存のJavaScript仮想マシンに実装可能な、メモリセーフでサンドボックス化された実行環境を記述しています。Webに埋め込まれたとき、WebAssemblyはブラウザの同一生成元と許可のセキュリティポリシーを実行します。その結果、WebAssemblyは、しばしばセキュリティの脆弱性を持つFlashプラグインよりも安全なソリューションとなります。
WebAssemblyは、C/C++などの言語からコンパイルすることができます。また、WebAssemblyはGoogle、Mozilla、Microsoft、Appleを中心としたW3Cのコミュニティグループによる共同作業であり、基本的に主要ブラウザベンダーを網羅しているので、Silverlightなどに比べて移植性が大幅に向上し、プラットフォームの互換性の問題も発生しなくなります。
Web プラットフォームの多くのプロジェクトでは、新しいネイティブ機能のサポートには、Web ブラウザや Runtime から複雑な標準化された API を実装する必要がありますが、JavaScript の API は遅いことが多いのです。WebAssemblyを使えば、これらの標準APIはよりシンプルに、より低いレベルで動作させることができます。例えば、顔認識のWebプロジェクトでは、データストリームへのアクセスはシンプルなJavaScript APIで実装し、顔認識ネイティブSDKが行うことはWebAssemblyに任せることができます。
WebAssemblyは、C/C++などの他言語をJavaScriptにコンパイルしたものではなく、ましてや新しいプログラミング言語でもないことを理解することが重要です。
探索
asm.js
上記のC言語の集計コードは、コンパイラがasm.jsを生成した後のコード3のようになります。
上記のコードは、WebAssemblyのテキスト形式に変換するのがやや複雑なので、理解を容易にするために、合理的なasm.jsから始めます(コード4参照)。
破棄されたテキストファイル
asm.jsのコードをWebAssemblyのテキスト形式add.westに変換します(コード5のように変換ツールはこの記事のツールチェインセクションを参照してください)。
WebAssemblyでロード可能で実行可能なコードの単位は、モジュールと呼ばれます。実行時に、モジュールはインポート値のセットでインスタンス化でき、モジュールの複数のインスタンスは同じ共有状態にアクセスできます。現在、テキスト形式のモジュールは、主にS式で表現されます。S式は正式なテキスト形式ではありませんが、ASTで表現することが容易です。webAssemblyはES6のモジュールとの統合も想定しています。
一つの論理的な関数定義は二つの部分を含みます:関数部分はモジュール内の各内部関数定義の署名を宣言し、スニペット部分は関数部分によって宣言された各関数の関数本体を含みます。webAssemblyは戻り値を持つ静的型付けであり、全てのパラメータは型を含んでいます。上記のadd.wistは次のように解釈できます。
- addという名前の関数を宣言しています。
- には2つのパラメータがあります。 <スパン <スパン a と b であり、いずれも32ビット整数である。
- は32ビット整数型である。
- 関数本体は32ビット加算です。
- 上記は、ローカル変数$aに取得された値です。
- 以下は、ローカル変数$bの取得値です。
- 明示的な return ノードがないので、return がこの加算関数の最終ロード命令となる。
バイナリWasmファイル
図1に示すように、C言語の集計コードはコンパイルされてバイナリファイルが生成され、これを読み進めることで対応するヘッダー、型、インポート、関数、コードセグメントを見つけることができる。WasmバイナリはJavaScript API経由でロードされ、最終的にマシンコードに変換されて実行されます。
ツールチェイン
開発者は、適切なツールチェインを使用して C / C ++ ソースファイルから WebAssembly モジュールをコンパイルできるようになりました。WebAssembly は、開発者がソースファイルや生成されたバイナリコンテンツをビルドして作業するのに役立つ多くのツールによってサポートされています。
<スパン エムズスクリプテン
Emscriptenは、図2に示すように、避けて通れないツールの一つである。図2では、Emscripten SDK マネージャ (emsdk) を使用して、複数の SDK およびツールを管理し、コードのコンパイルに現在使用されている特定の SDK およびツールのセットを指定しています。
Emscripten のメインツールは Emscripten compiler front-end (emcc) で、GCC などの標準的なコンパイラの簡単な代替実装である。
EmccはClangを使ってC/C++ファイルをLLVM(基盤となるLow Level Virtual Machineに由来)バイトコードに変換し、Fastcomp(Emscriptenのコンパイラコア、LLVMバックエンド)を使ってバイトコードをJavaScriptにコンパイルします。出力されたJavaScriptはNode.jsで実行したりHTMLに埋め込んでブラウザ上で動作させたりすることが可能です。この結果、CやC++のプログラムは、プラグインなしでJavaScript上で動作するようにコンパイルされます。
<スパン WABTとバイナリ
これに加えて、Emscripten などの他のツールで生成された WebAssembly バイナリを使用することに関心のある開発者のために、現在の http://webassembly.org/ さらに2種類のツールセットが公式に提供されています。
- WABT - WebAssembly Binary Toolkit(ウェブアセンブリ バイナリ ツールキット)。
- バイナリエン - コンパイラとツールチェイン。
WABTツールキットは、WebAssemblyのバイナリ形式を読みやすいテキスト形式に変換することをサポートしています。wasm2wast コマンドラインツールは WebAssembly のバイナリファイルを可読な S-expressionテキストファイルに変換します。was2wasm コマンドラインツールは、これと全く逆の処理を実行します。
Binaryenは、WebAssembly用のコンパイラおよびツールチェーン基盤ライブラリとしてC++で書かれた、より包括的なツールチェーンです(図3に示す)。webAssemblyはBinary形式で、Emscriptenと統合されているので、ツールはBinaryで始まり WebAssemblyを簡単に、速く、効率的にコンパイルできるように設計されています。以下のツールを含み、またそれだけではありません。
<イグ
- wasm-as: WebAssemblyをテキスト形式(現在はS-expression形式)からバイナリ形式にコンパイルします。
- wasm-dis: WebAssemblyをバイナリ形式からテキスト形式にデコンパイルします。
- asm2wasm: Emscripten の asm オプティマイザーを使って asm.js を WebAssembly のテキスト形式にコンパイルします。
- s2wasm: LLVMで開発され、新しいWebAssemblyバックエンドによって生成される.sフォーマット用のコンパイラです。
- wasm.js: インタプリタ、asm2wasm、S式パーサなど、JavaScriptにコンパイルされたバイナリアン・コンポーネントが含まれています。
バイナリエンは現在、WebAssemblyを生成するためのプロセスを2つ提供しています。emscriptenのasm.js生成はすでに非常に安定しており、asm2wasmはかなり簡単な処理であるため、C/C++をWebAssemblyにコンパイルする方法はすでに利用可能です(図4の通り)。
このように、Emscripten は Binaryen と共に、C/C++ から WebAssembly への完全なソリューションを提供します。また、BinaryenはWebAssemblyのツールチェーンエコシステムの強化に貢献します。
<スパン ヒント
WebAssemblyは活発に開発されているため、コンパイル手順やコンパイルツールに大きな変更や改良が加えられ、最終的にコンパイルツールや手順がより便利になる傾向があると考えられるので、開発者は公式サイトの最新情報に目を通す必要があります。
ハンズオン
Linux および mac OS プラットフォーム用のネイティブ コードを WebAssembly にコンパイルするには、以下の手順を実行します。
<スパン コンパイル環境の準備
OSにはコンパイラのツールチェーンが動作している必要があるため、Python、Node.js、Java環境(図5のようにJavaはオプション)に加え、GCCとcmake環境もインストールする必要があります。
Node.jsが他の方法でインストールされた場合、~/.emscriptenファイルのNODE_JS属性を更新する必要があるかもしれません。
正しいemscriptenのブランチをインストールする
ネイティブコードをWebAssemblyにコンパイルするためには、emscriptenのincomingブランチが必要です。emscriptenは単なるWebAssemblyのコンパイルツールチェーンではないので、正しいブランチを選択することが特に重要です(図6に示すように)。
ここで、URLTO固有のURLは https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz .
インストール時の例外処理
emcc -v コマンドを実行することで、インストールを確認することができます。図7のようなエラーが発生した場合は、JavaScriptバックエンドを持つLLVMコンパイラが生成されていないことを示しています。
図8の手順で、~/.emscriptenファイルの設定を以下のように変更することで、問題を解決することができます。
プログラムのコンパイル開始
これで完全なツールチェインが揃ったので、これを使って簡単なプログラムをWebAssemblyにコンパイルすることができます。ただし、他にいくつか注意点があります。
- は、パラメータ -s Wasm=1 を emcc に渡す必要があります (そうでない場合、デフォルトの emcc は asm.js をコンパイルしてしまいます)。
- Wasm バイナリと JavaScript ラッパーに加えて、emscripten に直接実行可能なプログラムの HTML ページを生成させたい場合は、拡張子が .html の出力ファイルを指定する必要があります。
コンパイルする前に、まず最小限のadd.cプログラムを用意します(コード6参照)。
add.cのプログラムをコード7のように編集し、以下のコマンドでコンパイルしてください。
WebAssemblyアプリケーションの実行
Chromeを例にとると、ローカルにあるHTMLファイルをブラウザ内で直接開くと、図9に示すようなエラーが発生します。
XMLHttpRequestのクロスドメインリクエストはfile://プロトコルをサポートしていないので、物理的にHTTPで出力する必要がありますが、これはPythonのSimplHTTPServerによって改善できます(コード8を参照)。
ブラウザで入力 http://127.0.0.1:8080 を開き、add.htmlを開くと、WebAssemblyに変換されたアプリケーションの出力を直接見ることができます。
<スパン スタンドアロンのWebAssemblyを作成する
デフォルトでは、emccはJavaScriptファイルとWebAssemblyの組み合わせを作成し、JSはコンパイルされたコードを含むWebAssemblyを読み込みます。C/C++開発者の場合、JavaScript開発者が呼び出すためのスタンドアロンWebAssemblyを作成することが望ましい場合があります。
上記のコマンドを実行すると、スタンドアロンのWasmファイルを取得することができます。なお、このパラメータはまだ開発中であり、いつでも仕様や実装が変更される可能性がある。
JavaScript APIコール
C/C++プログラムから.wasmモジュールをコンパイルした後、JavaScript開発者は.wasmファイルをロードして以下のように実行できます。WebAssemblyコミュニティグループは、非同期コンパイルと同様にStreamsによるストリーミングも使用する計画を持っています(コード10参照)。
最後の行はエクスポートしたWebAssembly関数を呼び出し、その関数がインポートしたJS関数を呼び出し、最終的にadd(201700, 2)を実行してコンソールに目的の結果を出力しています(図10の通り)。
パフォーマンス
では、実際にWebAssemblyの性能はどうなのでしょうか?まずはCPUのベンチマークとして使われてきたフィボナッチ級数を、性能の低い再帰的アルゴリズムを使って、Node.js v7.2.1でのWebAssemblyの性能優位性を見てみましょう(図11の通りです)。
最も基本的な1000ミリ秒の時間での総和計算の統計を見てみよう。同じコンピュータでFirefoxバージョン50.1.0を使用した場合の結果を図12に示します。
テストを繰り返すと結果は変わりますが、ブラウザーを再起動して複数のテストを平均化すると、やはり WebAssembly は JavaScript よりもほぼ一桁速いことがわかります。
デモ
図13は、WebAssemblyプロジェクトが公開したデモのうち、Unityゲームから移植した「Angry Bots Demo」です。
以下の方法で、WebAssemblyのパワーをブラウザで体験してください。Google Chrome の新しい安定バージョンはすでに WebAssembly をサポートしていますが、Canary バージョンと Firefox のナイトリーバージョンでテストすることをお勧めします。
-
でブラウザをダウンロードします。
1-1. グーグルクローム
1-2. Mozilla Firefoxです。
1-3. オペラ
1-4. ヴィヴァルディ -
WebAssemblyのサポートをオンにする。
2-1. Google Chrome: chrome://flags/#enable-webassembly.
2-2. Mozilla Firefox: about:config→accept→search for javascript.options.wasm→set を true に設定しました。
2-3. オペラ:opera://flags/#enable-webassembly。
2-4. Vivaldi: vivaldi://flags#enable-webassembly.
アクセスします。 http://webassembly.org/demo/ .
移動はW、A、S、Dキー、射撃はマウスのクリックで行います。WebAssemblyのゲームは、ブラウザ上で非常にスムーズに動作し、ネイティブのパフォーマンスに匹敵します。
WebAssembly は最新のブラウザから順次サポートされているほか、Intel Open Source Technology Center が開発した Crosswalk プロジェクト ( https://crosswalk-project.org/ )では、2016年11月上旬に早くもCrosswalk 22 stable release (Windows and Android platforms)でWebAssemblyの実験的サポートが追加され、開発者はこれを使用してAngry Bots Demoを体験することができます。
開発者の方へ
WebAssemblyはWebのパフォーマンスを大幅に向上させますが、開発者、特にフロントエンドやJavaScriptの開発者にとって、WebAssemblyがJavaScriptに取って代わるというわけではありません(図14に示すように)。
WebAssemblyはJavaScriptを補完するものとして設計されており、代替品ではありません。アプリケーションの重要な部分でネイティブに近いパフォーマンスを得るための方法を提供するものです。やがて、WebAssemblyによって(C/C++だけでなく)複数の言語がWebにコンパイルできるようになりますが、JavaScriptの勢いはこれによって衰えることはなく、Webの唯一の動的言語として存続するでしょう。また、WebAssemblyはJavaScriptエンジンのインフラの上に構築されているため、JavaScriptとWebAssemblyは多くの場面で一緒に使われることになるでしょう。
では、WebAssemblyはC/C++の開発者だけのものなのでしょうか?答えはやはりNOです。WebAssemblyの実装の最初の焦点はC/C++であり、Mozillaが主導して開発した、効率、セキュリティ、並列性に重点を置いたRustは、2016年末にWebAssemblyへのコンパイルに成功し、今後他の言語へのサポートも追加される予定です(コード11を参照)。
将来的には、ES6モジュールインターフェイスがJavaScriptに統合されれば、Web開発者はC++を書く必要がなく、他の人が書いたライブラリを直接活用することができ、C++のモジュールライブラリの再利用がJavaScriptのモジュールを使うのと同じくらい簡単にできるようになるでしょう。
進捗状況
開発ロードマップによると、2016年10月31日にWebAssemblyはブラウザプレビューのマイルストーンに到達しました。Google Chrome V8エンジン、Mozilla Firefox SpiderMonkeyエンジンともに、WebAssemblyブラウザプレビューサポートがトランクにあります。2016年12月末、Microsoft Edgeブラウザが採用しているJavaScriptエンジン、ChakraCore v1.4.0がWebAssemblyブラウザプレビューサポートに対応しました。そして、Webkit JavaScriptCoreエンジンもそのサポートに積極的です。
現在、WebAssembly Community Groupでは、初期(MVP)バイナリ形式のリリース候補と、複数のブラウザに実装されたJavaScript APIを公開しています。ブラウザプレビュー期間の一環として、WebAssembly Community Groupは現在、より広いコミュニティからフィードバックを募っています。Community Groupの最初の目標は、ブラウザプレビューが2017年の第1四半期に終了することですが、ブラウザプレビュー中に重要な発見があれば、サイクルが延長される可能性があります。ブラウザプレビューが終了すると、コミュニティグループはWebAssemblyの仕様案を作成し、ブラウザベンダーはデフォルトでその仕様に準拠した実装を提供し始めることができるようになります。2017年前半には、4つの主要ブラウザのWebAssemblyのネイティブサポートが安定版に到達することが期待されています。
Google V8 エンジンの最新開発に特化し、asm.js のコードは今後 Turbofan JavaScript コンパイラを通らず、WebAssembly にコンパイルされ、WebAssembly のネイティブ実行環境において最終マシンコードで実行されることになります。この変更により、Chrome で asm.js のプリコンパイル (AOT, Ahead Of Time Compilation) が可能になり、完全な後方互換性が実現されるなどのメリットがあります。新しい WebAssembly のコンパイルチャネルは、Turbofan JavaScript コンパイラのバックエンド部分の一部を再利用しているので、より少ないコンパイルと最適化の消費で、同様のコードを生成することができます。Google Chromeでは、Canaryバージョンで間もなくWebAssemblyがデフォルトで有効になり、開発チームは2017年第1四半期末までに安定版でリリースする予定です。
コミュニティ
W3CのWeb--Assembly Community Groupは、主要なブラウザベンダーの代表者が参加し、2015年4月下旬に結成されました。このグループのミッションは、Webに適した、ポータブルでサイズやロード時間の効率的な新しいフォーマットへのコンパイルについて、早期にブラウザ間のコラボレーションを促進することです。また、このコミュニティグループは、WebAssemblyをW3C Open Standardとして設計している最中です。現在、コミュニティグループには、記事で紹介したMozilla、Google、Microsoft、Appleの主要ブラウザベンダに加え、OperaのCTO、Intelからこの分野の専門家8名が参加している。もちろん、コミュニティグループのメンバーだけでなく、誰でも標準の開発に参加することができ、その中には https://github.com/WebAssembly 貢献する
今後の展望
大手ブラウザベンダーがWebAssemblyを積極的にサポートし、その機能を実装することで、オンラインゲーム、音楽、動画配信、AR/VR、プラットフォームエミュレーション、仮想マシン、リモートデスクトップ、圧縮・暗号化など、Web上で高いパフォーマンスを必要とするアプリケーションはネイティブに近いパフォーマンスを実現できるようになるでしょう。WebAssemblyは、Webの新時代を切り開くものと考えられています。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
ハートビート・エフェクトのためのHTML+CSS
-
HTML ホテル フォームによるフィルタリング
-
HTML+cssのボックスモデル例(円、半円など)「border-radius」使いやすい
-
HTMLテーブルのテーブル分割とマージ(colspan, rowspan)
-
ランダム・ネームドロッパーを実装するためのhtmlサンプルコード
-
Html階層型ボックスシャドウ効果サンプルコード
-
QQの一時的なダイアログボックスをポップアップし、友人を追加せずにオンラインで話す効果を達成する方法
-
sublime / vscodeショートカットHTMLコード生成の実装
-
HTMLページを縮小した後にスクロールバーを表示するサンプルコード
-
html のリストボックス、テキストフィールド、ファイルフィールドのコード例