URLに含まれる クエリストリング (クエリ文字列、クエリパラメータ) を取得し、使いやすい形に分解、整形する サンプルコード を掲載します。 単純な クエリストリング を分解する コード はよく見かけるので、もう少しがんばって 配列 や ハッシュ(連想配列) に対応したものを実装してみました。
目次
関連記事
URLに含まれる クエリストリング (クエリ文字列、クエリパラメータ) を取得し、使いやすい形に分解、整形する サンプルコード を掲載します。 単純な クエリストリング を分解する コード はよく見かけるので、もう少しがんばって 配列 や ハッシュ(連想配列) に対応したものを実装してみました。
目次
関連記事
「文字列 で記載された 関数 を 実行 する」方法は eval
を使いますが、
ここではその逆「関数 を 文字列化 する 方法」を記載します。
サンプルコード(関数 → 文字列)
var actFunc, strFunc; // 関数の実態 actFunc = function (a, b) { return a * b; }; // 関数の文字列化 strFunc = '' + actFunc; // 試しに表示してみる... window.alert(strFunc);
クロスドメイン制約 を 回避 する クライアント実装 を document.domain を利用して実現します。
クロスドメイン通信 を実現するには "サーバー側" と "クライアント側" の2カ所に対して手を入れる必要があるかどうか考えます。 ここでは、"クライアント側" に手を加えて クロスドメイン通信 を実現する方法を載せます。 具体的には、document.domain を変更することで信頼する範囲を広げて クロスドメイン通信 を行えるようにします。
Internet Explorer 10 以上 で対応している タッチイベント。
"タッチイベント" と表記しましたが、IE では マウス、タッチ、および ペン を一元的に Pointer イベント
として扱います。
ここでは IE の Pointer イベント
についてまとめます。
ちなみに・・・IE10 は、Chrome 等 で対応している touch イベント
に対応してないようです。。
そのイベント発生の取り回し方もやや違うようで、MSIE で 複数タッチ を利用する際は要注意です。
具体的な違いは、IE では touchmove
相当の MSPointerMove
が タッチの数ずつ発生し、それらを実装側で観測(pointerid
を利用して識別)しなければならない点です。
※ webkit の touch イベント については こちら。
Pointer イベント 対応 有無 の 確認
ブラウザ が Pointer イベント
が利用可能かどうか以下の JavaScriptコード で確認します。
if (window.navigator.msPointerEnabled) { // "Pointer" イベント が利用可能 }
デフォルト の タッチ動作 を 無効化
IE は デフォルト で パン、ピンチによるズーム、スワイプによる前/後ろページへ移動 等ができます。 ・・・できてしまう、が正しいんでしょうか? 他のブラウザではできないので、すべて無効化します。 無効化は以下の CSS 設定で可能です。
html, document, body { -ms-touch-action: none; }
webkit における タッチイベント の処理方法の基本です。 MSIE は 独自実装 してくれているのでそちらで対応します・・・
MSIE の タッチイベント については こちら。
WCF を 用いて RESTful サービス を作成していると、HTTP レスポンス ヘッダー を任意に変更 ── 場合によっては カスタムヘッダー を追加 ── することで、
クライアント側の処理を対応しやすくしたい場合があるかと思います。
ここでは、HTTP レスポンス ヘッダー を変更する サンプルコード を記載します。
基本的には OutgoingWebResponseContext クラス
を利用することで レスポンスヘッダー を変更できるようです。
※ WCF を 用いた RESTful サービス の構築方法 は こちら。
Internet Explorer 11 から UserAgent 文字列が変更になるそうです
(詳細は こちら)。
その結果、今まで MSIE
を使った Internet Explorer の判別が出来なくなります。
これでは困るので、従来の IE も含めて IE であることを判別できる JavaScript を以下で記載します。
Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv 11.0) like Gecko
Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; Touch; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; rv:11.0) like Gecko
Mozilla/5.0 (Windows NT 6.4; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; rv:11.0) like Gecko
var ua, isIE, array, version; // UserAgetn を小文字に正規化 ua = window.navigator.userAgent.toLowerCase(); // IE かどうか判定 isIE = (ua.indexOf('msie') >= 0 || ua.indexOf('trident') >= 0); // IE の場合、バージョンを取得 if (isIE) { array = /(msie|rv:?)\s?([\d\.]+)/.exec(userAgent); version = (array) ? array[2] : ''; }
Function.prototype.bind( context )
を用いると "スコープ (this) の固定" が出来ます。
JavaScript で オブジェクト指向 な開発を行っていると、"スコープ (this) の固定" は大変便利です。
ここでは 関数の詳細 と 使用例 (jQuery) を記載します。
概要
"this" および 引数 を固定して、新しい関数を生成します。
引数
引数
クロスドメイン通信 を実現するには "サーバー側" と "クライアント側" の2カ所に対して手を入れる必要があるかどうか考えます。
ここでは、"サーバー側" の設定変更で GET リクエスト に関してクロスドメイン通信を実現する方法を載せます。
具体的には、Access-Control-Allow-Origin
ヘッダー を無条件に返す設定をすることで、
jQuery 等 で クロスドメイン通信 を行えるようにします。
クロスドメイン通信 を実現するには "サーバー側" と "クライアント側" の2カ所に対して手を入れる必要があるかどうか考えます。 ここでは、"クライアント側" ── 特に IE8, IE9 ── において クロスドメイン通信 を実現する 実装方法 を記載します。
IE8、 IE9 で クロスドメイン通信 を行う場合、 XDomainRequest を利用します。 この XDomainRequest は XMLHttpRequest と似て非なるもの(詳細は こちら)なので、そのまま jQuery.ajax で使うことが出来ません。 jQuery の バグトラック によると、この違いは プラグイン で吸収するそうです。 で、その プラグイン と 使い方 は以下の通りです。
Internet Explorer 系列 で クロスドメイン通信 を行う場合、
IE8, IE9 だと XDomainRequest
、IE10 では XMLHttpRequest
を利用します。
ここでは、似て非なる両者を比べてその違いをまとめます。
(IE7 以前は調べていません。。)
ちなみに、Chrome や Firefox はかなり以前から XmlHttpRequest level 2 (クロスドメイン通信) 対応しているようです(詳細はこちら)。
XDomainRequest は XMLHttpRequest level 2 に比べて、以下の制約があるようです。
JavaScript を用いて クロスドメイン で リクエストを行いたいと思うと、 "同一制限元ポリシー(Same Origin Policy)" に引っかかります。 そもそもその制約にある背景は何か…といったところから、制約内容、制約の例外、制約の回避についてまとめました。
クロスサイトリクエストフォージェリ(CSRF)対策。 悪意あるサイトからの不正な要求を拒否するため。
制約の回避方法を見ていてもそうですが、信頼できるサーバーかのリクエストしか受け付けないようになっているようです。
Chrome 11 以降の UserAgent に関して調べてみたので、ここにまとめます。 プラットフォーム周りが知りたかったのですが、どうも Internet Explorer と互換するようです。
Windows NT x.x
でプラットフォームが分かる。
Windows NT 6.2
の場合、Windows 8Windows NT 6.1
の場合、Windows 7Windows NT 6.0
の場合、Windows VistaWindows NT 5.1
の場合、Windows XPWOW64
Win64; x64
Win64; IA64
32bit Windows 7 - 32bit Chrome 28
Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36
64bit Windows 7 - 32bit Chrome 28
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36
64bit Windows 7 - 64bit Chrome 28
Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36
Mac OS X
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.151 Safari/535.19
Linux
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.16 Safari/534.24
今回、参考にしたサイトは以下の通りです。
Blogger の 記事 一覧 を表示する ページ を作成します。 ここで紹介する方法では、ソート順(タイトル名、公開日付、更新日付)、表示方法(一覧、ラベル別)を任意に指定できます。 また、各要素に クラス名 が割り振られているので、好きな表示を簡単に実現できます。
とりあえず自分のブログで試したい方は デモページ(日本語) をご利用ください。
(…いくつかの ブログ で "記事一覧を表示する方法" が記載されていましたが、 個人的にしっくりこなかったので新しく作ってしまえ、という経緯です。。)
Blogger の 左メニュー から [ページ] を選択します。
[新しいページ] を選択します。
編集ページにおいて、以下の ソースコード を コピー して貼り付けます。
<script type="text/javascript"> var POSTSTOC_SETTINGS = { blogURL: 'garafu.blogspot.jp', // Blogger の ドメイン を設定 maxResults: Infinity, // 最大受信記事数 を設定。Infinity ですべての記事を受信 sort: { key: 'published', // ソートキー [title, published, updated] order: 'default' // ソート順 [default, asc, desc] }, printby: 'title', // 表示方法 [title, label, label.nameorder, label.contentsorder] newPost: { enabled: true, // 新着記事アイコンを表示するかどうか symbol: 'NEW !', // 新着記事の表示文字列 term: 30 // 新着記事判定する日数 }, thumbnail: { enabled: true, // サムネイル表示するかどうか noImageURL: '//garafu.github.io/blogger.toc/release/0.0.5/noimage.png' // サムネイルが存在しないときに割り当てる画像 }, published: { enabled: true, // 公開日を表示するかどうか format: 'yyyy/MM/dd' // 公開日のフォーマット }, updated: { enabled: true, // 更新日を表示するかどうか format: 'yyyy/MM/dd' // 更新日のフォーマット } }; </script> <link rel="stylesheet" type="text/css" href="//garafu.github.io/blogger.toc/release/0.0.12/simple.css" /> <script type="text/javascript" src="//garafu.github.io/blogger.toc/release/0.0.12/blogger.toc.min.js"></script>
以下の設定を変更します。細かな設定に関しては後述の説明をご確認ください。
blogURL | Blogger の ドメイン を設定します。 |
---|---|
sort.key |
記事ソート順を [title, published, updated] から設定します。デフォルト published。
|
printby | 表示方法を [title, label, label.nameorder, label.contentsorder] から設定します。デフォルト label。
|
thumbnail.enabled | サムネイルを表示するかどうか を設定します。デフォルト false 。 |
|
公開日を表示するかどうか を設定します。デフォルト false 。 |
updated.enabled | 更新日を表示するかどうか を設定します。デフォルト false 。 |
保存して公開します。
とりあえず試したい方は デモページ(日本語) をご参照ください。 試した結果が出力されるので、そのコードをコピー&ペーストすると簡単に "Blogger 記事 の 一覧表示" を作成できます。
{ blogURL: 'garafu.blogspot.com', maxResults: Infinity, keyword: 'JavaScript', sort: { key: 'published', order: 'default' }, printby: 'label', newPost: { enabled: false, symbol: 'NEW !', term: 30, target: 'published' }, thumbnail: { enabled: false, noImageURL: '//garafu.github.io/blogger.toc/release/0.0.5/noimage.png' }, published: { enabled: false, format: 'yyyy/MM/dd HH:mm:ss' }, updated: { enabled: false, format: 'yyyy/MM/dd HH:mm:ss' }, target: undefined }
プロパティ | 型 | 説明 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
blogURL |
string |
ブログのドメインを指定します。
https://garafu.blogspot.com/ であれば garafu.blogspot.com 部分のみを指定します。 |
||||||||
maxResults |
number |
受信する最大記事数を指定します。すべての記事を対象とする場合 Infinity を指定します。
デフォルト Infinity です。 |
||||||||
keyword |
string |
絞り込みたいキーワードを指定します。 日本語タグを指定する場合 URI エンコード を行ったものを指定します(デモページでは自動変換するので日本語のままでOKです)。 何も指定しなければすべてのタグを受信対象にします。 デフォルトは指定なしです。 | ||||||||
sort |
object |
ソート方法に関する設定を指定します。 | ||||||||
sort.key |
enum |
ソートキーを指定します。指定できる値は以下の通りです。
|
||||||||
sort.order |
enum |
ソート順を指定します。指定できる値は以下の通りです。
| ||||||||
printby |
enum |
表示方法を指定します。指定できる値は以下の通りです。
|
||||||||
newPost |
object |
新着記事表示に関する設定を指定します。 | ||||||||
newPost.enabled |
boolean |
新着記事アイコンを表示するかどうかを指定します。 | ||||||||
newPost.symbol |
string |
新着記事アイコンとして表示する文字列を指定します。 | ||||||||
newPost.term |
number |
新着記事として判定する期間(日数)を指定します。 | ||||||||
newPost.target |
enum |
新着記事として判定するとき、比較対象とする日付を指定します。指定できる値は以下の通りです。
|
||||||||
thumbnail |
object |
サムネイル表示に関する設定を指定します。 サムネイルは記事中の一番最初にある画像が使用されます。 サムネイルは通常 72px × 72px の大きさです。 | ||||||||
thumbnail.enabled |
boolean |
サムネイルを表示するかどうかを指定します。 | ||||||||
thumbnail.noImageURL |
string |
サムネイルが存在しない場合に表示する画像へのパス(URL)を指定します。 | ||||||||
published |
object |
公開日に関する設定を指定します。 | ||||||||
published.enabled |
boolean |
公開日を表示するかどうかを指定します。 | ||||||||
published.format |
string |
公開日を表示する場合、どのように表記するかを指定します。 利用できる制御文字は (*1) 日付フォーマット をご参照ください。 | ||||||||
updated |
object |
更新日に関する設定を指定します。 | ||||||||
updated.enabled |
boolean |
更新日を表示するかどうかを指定します。 | ||||||||
updated.format |
string |
更新日を表示する場合、どのように表記するかを指定します。 利用できる制御文字は (*1) 日付フォーマット をご参照ください。 | ||||||||
target |
string |
リンクの開き方( a タグ の target 属性 )を指定します。デフォルトは undefined で指定なしです。 |
制御文字 | 説明 | サンプル |
---|---|---|
d | 月の日にち (1 ~ 31)。 | 3 |
dd | 月の日にち (01 ~ 31)。 | 03 |
E | 曜日の省略名。 | 火 |
EE | 曜日の完全名。 | 火曜日 |
G | 時期または時代 (年号)。 | 西暦 |
h | 12時間形式の時間 (1 ~ 12)。 | 5 |
hh | 12時間形式の時間 (01 ~ 12)。 | 05 |
H | 24時間形式の時間 (1 ~ 24)。 | 8 |
HH | 24時間形式の時間 (01 ~ 24)。 | 08 |
m | 分 (0 ~ 59)。 | 2 |
mm | 分 (00 ~ 59)。 | 02 |
M | 月 (1 ~ 12)。 | 6 |
MM | 月 (01 ~ 12)。 | 06 |
MMM | 月の省略名。 | 1 |
MMMM | 月の完全名。 | 1月 |
s | 秒 (1 ~ 59)。 | 6 |
ss | 秒 (01 ~ 59)。 | 06 |
t | AM/PM 指定子。 | 午前 |
yy | 2桁の年。 | 98 |
yyyy | 4桁の年。 | 1998 |
z | UTCを基準とする時間単位のオフセット (先行ゼロなし)。 | -8 |
zz | UTCを基準とする時間単位のオフセット (先行ゼロ付きの 1桁の値)。 |
-08 |
zzz | UTCを基準とする時間および分単位のオフセット。 | -08:00 |
スタイルシート
各要素に対してできるだけ クラス名 を割り振るよう作成されています。
あらかじめある スタイルシート で思うような表示ができていない場合、
<link />
を利用せず、自前で stylesheet を記述することができます。
不具合、ご要望等はこの記事にレスしていいただくか、こちら(Github garafu/blogger.toc Issues) までご連絡いただけると幸いです。
参考記事
Blogger の フィード URL (RSS や Atom、JSON を受信する URL)を作成する Webアプリ です。 * は必須入力項目です。 必要事項を入力した後、「フィード URL 生成」で フィード URL が生成されます。
* | ブログURL | |
* | リソース | |
* | 本文 | |
ラベル | ||
応答フォーマット | ||
取得開始番号 | (1 から始まる数値) | |
最大取得数 | ||
ソート順 | ||
公開日付 |
開始
(例:2013-07-10T00:00:00)
終了
|
|
更新日付 |
開始
(例:2013-07-10T00:00:00)
終了
|
|
リダイレクト | ||
コールバック | ||
|
別ウィンドウで フィードを開く |
Google closure tools に含まれる closure compiler の使い方です。 closure library に含まれる closurebuilder.py を利用して、 複数の JavaScript ファイル を 1つの JavaScript ファイル へ バッチ処理で コンパイル します。 ここでは、バッチ処理する前提となる「フォルダ構成」と「コンパイルバッチ」を記載します。
(※Windows で、Javaランタイム、Python が インストール 済み の環境を前提とします。)
フォルダ構成
ROOT ├ lib │ ├ closure-compiler │ │ └compiler.jar
… コンパイラ本体 │ └ closure-library │ └ closure │ ├ bin │ │ ├ build │ │ │ └closurebuilder.py
… コンパイルで利用 │ │ ├ calcdeps.py │ │ └ scopify.py │ ├ css │ └ goog │ └ closure library の ソースコード ├ src │ └ソースコード
… 自分が作成したソースコードの保存場所 └build.bat
… ビルドを実行するバッチファイル
build.bat
@echo off rem 実行ディレクトリをバッチファイルの場所へ移動。 cd /d "%~dp0" rem 環境変数に Java と Python へのパスを追加。 set PATH=%PATH%;C:\Program Files\Java\jre7\bin set PATH=%PATH%;C:\Python27 rem 各種変数を設定。 set CLOSUREBUILDERPY=.\lib\closure-library\closure\bin\build\closurebuilder.py set CLOSURECOMPILERJAR=.\lib\closure-compiler\compiler.jar set CLOSURE_LIBRARY_DIR=.\lib\closure-library\closure\goog set CLOSERU_LIBRARY_THIRD_DIR=.\lib\closure-library\third_party\closure\goog set SOURCE_CODE_DIR=.\src set MAIN_CLASS_NAME=garafu.test set OUTPUT_FILE=.\build\garafu.test.min.js rem コンパイルの実行。 %CLOSUREBUILDERPY% ^ --root=%CLOSURE_LIBRARY_DIR% ^ --root=%CLOSERU_LIBRARY_THIRD_DIR% ^ --root=%SOURCE_CODE_DIR% ^ --namespace=%MAIN_CLASS_NAME% ^ --output_mode=compiled ^ --compiler_jar=%CLOSURECOMPILERJAR% ^ --compiler_flags="--compilation_level=ADVANCED_OPTIMIZATIONS" ^ --compiler_flags="--js_output_file=%OUTPUT_FILE%"
「各種編集の設定」(12 - 19 行)では build.bat
からの相対パスを指定します。
フォルダ構成が上記と異なる場合、この部分を変更することになります。
%CLOSERU_LIBRARY_THIRD_DIR%
設定を行わないと場合により、次のようなエラーが発生します。
closure compiler は不要なファイル、コードを削ってくれるので、余分に見えても追加しておきます。
(不要なファイル、コードを削るのは ADVANCED_OPTIMIZATIONS に限りますが。。)
depstree.NamespaceNotFoundError: Namespace "goog.async.Deferred" never provided. Required in Source [JavaScript ファイル への パス]
%MAIN_CLASS_NAME%
最初に実行するクラス名を指定します。
基本的に、ここで指定するクラスは singleton で、
goog.addSingletonGetter([クラス名]); および
[クラス名].getInstance();
の実装があるクラスを指定します。
この部分は作成しているプロジェクト環境に合わせて変更します。
%OUTPUT_FILE%
出力先のフォルダ、ファイル名を指定します。
ここでは、 build フォルダ以下にファイルを作成するようにしています。
この部分は作成しているプロジェクト環境に合わせて変更します。
今回、参考にしたサイトは以下の通りです。
IE8、IE9、IE10 の userAgent について、ブラウザモードによる違い、プラットフォームによる違いを調査してみました。 調査結果はダイジェスト(まとめ)と具体的な一覧にしてみました。
MSIE 7.0
と Trident/x.0
が同時に存在する。
Trident/6.0
の場合、IE10Trident/5.0
の場合、IE9Trident/4.0
の場合、IE8Trident
がない場合、IE7Tablet PC x.0
が存在する。WOW64
が存在する。Win64
が存在する。Windows NT x.x
でプラットフォームが分かる。
Windows NT 6.1
の場合、Windows 7Windows NT 6.0
の場合、Windows VistaWindows NT 5.1
の場合、Windows XP表は横にスクロールしてご確認ください。
調査環境は Windows 7 32bit ノートPC(IE8-10)、Windows 8.1 64bit ノートPC(IE11) です。
IE | ブラウザモード |
---|---|
IE10 | IE10 |
IE10互換表示 | |
IE9 | |
IE8 | |
IE7 | |
IE9 | IE9 |
IE9互換表示 | |
IE8 | |
IE7 | |
IE8 | IE8 |
IE8互換表示 | |
IE7 |
Mozilla | MSIE | Trident | userAgent |
---|---|---|---|
5.0 | 10.0 | 6.0 |
Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
4.0 | 7.0 | 6.0 |
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
5.0 | 9.0 | 5.0 |
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Tablet PC 2.0) |
4.0 | 8.0 | 4.0 |
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
4.0 | 7.0 |
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
|
5.0 | 9.0 | 5.0 |
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Tablet PC 2.0) |
4.0 | 7.0 | 5.0 |
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
4.0 | 8.0 | 4.0 |
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
4.0 | 7.0 |
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
|
4.0 | 8.0 | 4.0 |
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
4.0 | 7.0 | 4.0 |
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
4.0 | 7.0 |
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0) |
参考記事
JavaScript で 非同期通信 をしようとしたとき、その通信結果をクライアント側でキャッシュしたりしなかったり… 実際のところどうなっているか、どうやったら良いのかを見ていきます。
まずは仕様について見てみます。
いわゆる、XMLHttpRequest
の仕様 ──
W3C の ワーキングドラフト の キャッシュ に関する部分(send メソッド の下の方)
──
では以下のようなことが述べられています。
Cache-Control
や Pragma
を勝手に付加しない304 Not Modified
のときは、200 OK
と同じ挙動をするでは、これらを元に、キャッシュする 通信方法 と、キャッシュしない 通信方法 はどうしたら良いかを、 具体的なサンプルコードを交えながら以下で見ていきます。
何もしなければ、デフォルトでキャッシュされます。
通信した内容がブラウザにキャッシュされて、キャッシュされたデータを使った 200 OK
が返ってきます。
サンプルコード(サーバー側:C# & ASP.NET MVC)
public ActionResult Test() { // レスポンス用のサンプルデータを作成 var o = new { Message = "foo-bar", DateTime = DateTime.Now.ToString("r") }; // 作成したデータを返信 return this.Json(o, JsonRequestBehavior.AllowGet); }
サンプルコード(クライアント側:JavaScript)
var xhr = new XMLHttpRequest(); var url = 'http://localhost/BrowserCacheTest/Home/Test'; var responseText = ''; xhr.open('GET', url, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { responseText = xhr.responseText; } }; xhr.send();
通信の様子
このシーケンスだけだと分かりませんが、上記サンプルコードの リクエスト を何度も実行しています。 つまり、キャッシュされた通信の場合、以下のような特徴があると言えそうです。
200 OK
を受け取る無意味なクエリパラメータを末尾につけて URL を異なるものにすることで、キャッシュしない… という方法もありますが、ここでは HTML通信 の ヘッダ にキャッシュ制御を付記することで「キャッシュ しない」を実現します。
「キャッシュ する」場合との違いは、
サーバー側でレスポンスヘッダーに Cache-Control: no-cache
と Pragma: no-cache
を記述するようにしている点です。
これらをサーバー側で指定することによって、クライアント側は該当する通信をキャッシュしなくなります。
サンプルコード(サーバー側:C# & ASP.NET MVC)
public ActionResult Test() { // レスポンス用のサンプルデータを作成 var o = new { Message = "foo-bar", DateTime = DateTime.Now.ToString("r") }; // キャッシュしないようヘッダーを追加 this.Response.Cache.SetCacheability(HttpCacheability.NoCache); this.Response.AddHeader("Pragma", "no-cache"); // 作成したデータを返信 return this.Json(o, JsonRequestBehavior.AllowGet); }
サンプルコード(クライアント側:JavaScript)
var xhr = new XMLHttpRequest(); var url = 'http://localhost/BrowserCacheTest/Home/Test'; var responseText = ''; xhr.open('GET', url, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { responseText = xhr.responseText; } }; xhr.send();
通信の様子
通信がキャッシュされず、毎回取得し直されていることが分かります。
また、レスポンスヘッダーに Cache-Control: no-cache
、 Pragma: no-cache
、 Expires: -1
が指定されていることも分かります。
というわけで…ブラウザにキャッシュさせたくない場合、サーバー側の レスポンス ヘッダー を指定することで実現できるようでした。
WEBアプリケーション 構築 をしていると、データ更新したはずなのに ブラウザ に反映されない!! ってときがあるかと思います。 ここでは、「新しい情報が取得できない」問題に対する HTTP通信 キャッシュ の 無効化 方法を幾通りか載せます。 状況に応じてどれか、または、いくつかを選択して キャッシュ を 無効化 してください。
静的コンテンツ
動的コンテンツ
個人的な環境、趣味、その他の都合により、Windows よりな対策になっていますがご了承ください。。
次に示す METAタグ を head
セクションに記載します。
サンプルコード(HTML)
<meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Pragma" content="no-cache">
この設定を行うことで、指定 URL 以下にぶら下がる コンテンツ、通信はすべて キャッシュ が 無効化 されます。
Cache-Control: no-cache, no-store
Pragma: no-cache
特定の MIME を キャッシュ しないようにする方法もあるようですが… どうしてか、うまくいきませんでした。。
サーバー処理において、描画が始まる直前に、レスポンスヘッダーにキャッシュを無効化する内容を記載します。 以下の サンプルコード 中、11~12行目 が キャッシュ を 無効化 する コード です。
ここでは、 C# & ASP.NET という環境で書いていますが、他の言語(Java, PHP, Ruby 等)でも同様のことができます。
サンプルコード(C# & ASP.NET MVC)
public ActionResult Test() { // レスポンス用のサンプルデータを作成 var o = new { Message = "foo-bar", DateTime = DateTime.Now.ToString("r") }; // キャッシュしないようヘッダーを追加 this.Response.Cache.SetCacheability(HttpCacheability.NoCache); this.Response.AddHeader("Pragma", "no-cache"); // 作成したデータを返信 return this.Json(o, JsonRequestBehavior.AllowGet); }
静的コンテンツ でも 動的コンテンツ でも、どちらでも対応できる方法です。
jQuery.ajax
の オプション にある cache: false
設定がこの方法に相当します。
サンプルコード(JavaScript)
var xhr = new XMLHttpRequest(); var url = 'http://localhost/BrowserCacheTest/Home/Test'; var responseText = ''; // URL に 無意味な クエリパラメータ を付加する url += '?'; url += (new Date()).getTime(); // Chrome は動作が速すぎて時間が等しくなるため seed をつけて回避します if (!window.seed) { window.seed = 0; } url += window.seed++; // XMLHttpRequest を実行 xhr.open('GET', url, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { responseText = xhr.responseText; } }; xhr.send();
まとめ。
…いろいろと例を書いてきましたが、 結局、以下に示す内容が サーバー からの レスポンス ヘッダー に記載されていると キャッシュ が 無効化 されます。
Cache-Control: no-cache, no-store
Pragma: no-cache
今回、参考にしたサイトは以下の通りです。
業務アプリを開発していると良く遭遇するこの問題…。 基本的には HTML のトップレベルで対応することで対策が無難と思います。 いわゆるホワイトリスト方式(基本は拒否。許可するものだけリストアップ。)です。
禁止したい機能は以下のようなものを想定しています。
サンプルコード with jQuery
<html> <head> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript"> // document (トップレベル) に対して処理するところが ポイント $(document).on('mousedown', function (event) { // テキスト選択処理を無効化 return false ; }).on('selectstart', function () { // テキスト選択処理を無効化 return false ; }).on('contextmenu', function (event) { // 右クリックメニュー禁止 return false ; }).on('mousewheel', function (event) { // Ctrl + wheel による拡大縮小禁止 return !event.ctrlKey; }).on('dragover', function (event) { // ドラッグ&ドロップ禁止(テキスト、画像等の D&D によるページ遷移を防ぐ) return false ; }).on('drop', function (event) { // ドラッグ&ドロップ禁止(テキスト、画像等の D&D によるページ遷移を防ぐ) return false ; }); </script> </head> <body> <h1>メロンパン。</h1> </body> </html>
各禁止項目の詳細を以下で詳しく見ていきます。
$(document).on('mousedown', function (event) { return false ; }).on('selectstart', function () { return false ; });
この処理で、必要な処理まで無効になってしまいます。 例えば、テキストボックスにおいてクリックによるキャレット移動、文字列選択が無効になります(下記デモ参照)。 キャレット移動、文字列選択を可能にするためには、有効化したいテキストボックスに対してバブルアップを禁止するようにします(下記サンプルコード参照)。
デモ (テキスト選択 禁止)
この DIV 領域内 はテキスト選択無効。 この領域外から D&D して選択は可能です。 それ故、テキスト選択を完全無効化するためには、 document に対して拒否する処理を記述する必要があります。
テキストボックスの選択可否:
選択不可
サンプルコード (キャレット移動、テキスト選択の許可)
$('#textbox').on('mousedown', function (event) { event.stopPropagation(); }).on('selectstart', function (event) { event.stopPropagation(); });
$(document).on('contextmenu', function (event) { return false ; });
右クリック メニュー (コンテキストメニュー) を明示的に禁止することで、 独自実装を行ったり、別機能を割り当てたりすることができるようになります。
$(document).on('mousewheel', function (event) { return !event.ctrlKey; });
この実装、IE では有効ですが、残念ながら Chrome は正常に禁止できませんでした。。 Firefox、Opera 等は確認していないので不明です。
$(document).on('dragover', function (event) { return false ; }).on('drop', function (event) { return false ; });
ブラウザ の テキストボックス に メモ帳 等からテキストを D&D したり、 ブラウザ 画面内に 画像ファイル を D&D したりすると、ペーストや画面遷移を起こしてしまいます。 この実装では、そうした問題に対して対策となります。