5. 操作性向上・制御¶
この章では、操作性向上のため、各種ライブラリを用いて操作を補助したり制限する方法を説明する。
5.1. ショートカットキー制御¶
5.1.1. 概要¶
ショートカットキー制御とは、指定したキー操作を無効化したり、動作を変更することである。
ここでは、Mousetrapを用いて、 Ctrl + c および Ctrl + v による「コピーアンドペースト」、および Ctrl + s による「ページの保存」の動作を変更する方法を説明する。
利用ライブラリ | サンプル | 参考ページ |
---|---|---|
Mousetrap | コピー・ペースト無効化 | Mousetrap - Keyboard shortcuts in Javascript |
5.1.2. 利用方法¶
HTMLでは、Mousetrapと、ショートカットキーを制御するために実装したJavaScript(disable-copy-paste.js)を読み込む。
留意点として、Mousetrapの仕様上、textarea
・input
・select
要素にフォーカスがある場合は、キー操作時の動作は変更されない。
これらの要素内でキー操作を変更する場合は、対象の要素にmousetrap
クラスを指定する必要がある。
<textarea id="area1">No key-controls executed for this text-area.</textarea>
<!-- (1) -->
<textarea id="area2" class="mousetrap">key-controls for copy(Ctrl + c) and paste(Ctrl + v) is disabled for this text-area.</textarea>
<!-- (1) -->
<textarea id="area3" class="mousetrap">disable copy(Ctrl + c) and paste(Ctrl + v), and execute original function when key-control for save(Ctrl + s) is used.</textarea>
<!-- (2) -->
<script src="../lib/vendor/mousetrap/1.6.2/mousetrap.min.js"></script>
<script src="js/disable-copy-paste.js"></script>
項番 | 説明 |
---|---|
(1)
|
キー制御を有効化するため class属性に “mousetrap” を指定する。
|
(2)
|
Mousetrapと、独自に実装したJavaScriptを読み込む。
|
JavaScript(disable-copy-paste.js)では、Mousetrap.bind
関数を用いて次の処理を実行する。
// disable-copy-paste.js
'use strict';
window.onload = function () {
// (1)
Mousetrap.bind('ctrl+c', function () {
return false;
});
// (2)
Mousetrap.bind('ctrl+v', function () {
return false;
});
// (3)
Mousetrap.bind('ctrl+s', function () {
if (document.activeElement.id === 'area3') {
alert('data is saved');
return false;
}
return true;
});
};
項番 | 説明 |
---|---|
(1)
|
Ctrl + c 実行時の操作を変更する。ここでは操作を無効化する。
|
(2)
|
Ctrl + v 実行時の操作を変更する。ここでは操作を無効化する。
|
(3)
|
Ctrl + s 実行時の操作を変更する。ここではフォーカスが #area3 にある場合のみ処理を変更する。
|
標準動作を無効化するためにはMousetrap.bind
関数のコールバック関数でfalse
を返す。true
を返すと標準動作が行われる。
Note
ショートカットキー操作で起動できる動作は、JavaScriptで実行できる動作のみである。 例えば、 Ctrl + s 操作による「ページの保存」のようなブラウザの標準動作は、JavaScriptから実行できないため、これを別のショートカットキー操作に割り当てることは不可能である。
Note
対応可能なキーは Mousetrapの公式リファレンス の「Supported Keys」を参照すること。なお、「F12」など、「Supported Keys」に記載は無いが、制御可能なものもある。Mousetrapのソースコードを参照すること。
5.2. 範囲選択防止¶
5.2.1. 概要¶
user-select
プロパティを使用する。user-select
プロパティは各ブラウザが独自に実装しており、設定がそれぞれ異なる。また、 Internet Explorer は Firefox ・ Chrome と比べてハイライトの挙動に差異がある。Note
user-select
プロパティは World Wide Web Consortium (W3C) CSS Level 3(https://www.w3.org/TR/css-ui-3/)に含まれておらず、CSS Level 4(https://www.w3.org/TR/css-ui-4/)に記述されているものの、草案(Working Draft)に留まっている。
Warning
後述する範囲選択防止の実現のため用いるCSSやJavaScriptは、ブラウザ毎に挙動が異なり、またブラウザバージョンアップ後に動作が保証されているわけではない。本ガイドラインで紹介する実現方法については動作確認しているが、ブラウザのバージョンアップなども考慮した上で、範囲選択防止の要件を取り込むかどうか検討すること。
利用ライブラリ | サンプル | 参考ページ |
---|---|---|
jQuery | - |
5.2.2. 利用方法¶
5.2.2.1. ページ全体の範囲選択防止¶
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="js/prevent-range-selection-all-elements.js"></script>
html
要素にcss
関数を用いてuser-select
プロパティを指定する。// (1)
$('html').css({
'user-select' : 'none',
// (2)
'-moz-user-select' : 'none',
// (3)
'-webkit-user-select' : 'none',
// (4)
'-ms-user-select' : 'none'
});
項番 | 説明 |
---|---|
(1)
|
CSSによる範囲選択防止を実装する。
|
(2)
|
ブラウザがFirefoxの場合の定義を設定する。
|
(3)
|
ブラウザがSafari、Chromeの場合の定義を設定する。
|
(4)
|
ブラウザがInternet Explorerの場合の定義を設定する。
|
5.2.2.2. ページ内の特定要素の範囲選択防止¶
disable
を設定する。なお、クラス名はJavaScriptのセレクタと一致していれば他の名称でも問題ない。<p class="disable">
HTMLの特定の要素を選択不可とします
</p>
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="js/prevent-range-selection-target-elements.js"></script>
disable
を設定した要素にcss
関数を用いてuser-select
プロパティを指定する。// (1)
$('.disable').css({
'user-select' : 'none',
'-moz-user-select' : 'none',
'-webkit-user-select' : 'none',
'-ms-user-select' : 'none'
});
項番 | 説明 |
---|---|
(1)
|
CSSによる範囲選択防止。
class="disable" を設定した要素をハイライトできないように実装する。 |
Note
INPUT・TEXTAREAにuser-select
プロパティを設定した場合、ブラウザ毎に挙動が異なるため設定しないこと。
- Ctrl + a を押下する。
- ハイライト可の要素から不可の要素をマウスでドラッグする。
keydown
イベントをトラップし、 Ctrl + a が押下された場合にfalse
を返却する。// (1)
$(document).on('keydown', keyDownEvent);
// (2)
function keyDownEvent(event) {
// (3)
if (!isIE()) {
return true;
}
// (4)
if (event.ctrlKey && event.key === 'a') {
// (5)
if (event.target.nodeName !== 'INPUT' &&
event.target.nodeName !== 'TEXTAREA') {
// (6)
return false;
}
}
return true;
}
項番 | 説明 |
---|---|
(1)
|
キー押下実行時に関数を呼び出す。
|
(2)
|
キー操作が行われた場合に実行する関数を定義する。
|
(3)
|
独自関数によるブラウザ判定を実施し、Internet Explorer以外の場合は処理を終了する。
|
(4)
|
キー押下が Ctrl + a の場合の条件を実装する。
|
(5)
|
入力系の要素は制御対象外とする。
|
(6)
|
入力系以外の要素の場合にはキー操作を無効とする。
|
Note
Ctrl + a の制御はMousetrapを用いて実現することもできる。ショートカットキー制御 を参照すること。
selectstart
イベント発生時に範囲選択開始時点の要素を取得し、mouseover
イベント発生時に開始時点の要素と一致しているかチェックする。不一致の場合は範囲選択開始時点の要素を格納する。また、mouseup
イベント発生時に変数を初期化する。// (1)
$(document).on('mouseup', mouseUpEvent);
$(document).on('selectstart', mouseSelectEvent);
$(document).on('mouseover', mouseOverEvent);
// (2)
var select = false;
var range = null;
var selectStartNode = '';
// (3)
function mouseUpEvent() {
if (!isIE()) {
return true;
}
// (4)
select = false;
range = null;
selectStartNode = '';
return true;
}
// (5)
function mouseSelectEvent(event) {
if (!isIE()) {
return true;
}
var selection = window.getSelection();
// (6)
if (selection.rangeCount === 0) {
return true;
}
// (7)
if (event.target.tagName === 'HTML' ||
event.target.tagName === 'BODY') {
selection.removeAllRanges();
return false;
}
// (8)
select = true;
range = selection.getRangeAt(0);
selectStartNode = selection.anchorNode.parentNode;
return true;
}
// (9)
function mouseOverEvent(event) {
if (!isIE()) {
return true;
}
// (10)
if (select && event.target !== selectStartNode) {
var selection = window.getSelection();
var newRange = document.createRange();
newRange.selectNode(range.startContainer);
selection.removeAllRanges();
// (11)
selection.addRange(newRange);
}
return true;
}
項番 | 説明 |
---|---|
(1)
|
マウス操作時に範囲選択を防止するための関数を実行する。
|
(2)
|
範囲選択制御に使用する変数(範囲選択ステータス、範囲選択開始時の領域、範囲選択開始時の要素名)を定義する。
|
(3)
|
マウスキーが離された時に実行する関数を定義する。
|
(4)
|
範囲選択制御に使用する変数を初期化する。
|
(5)
|
範囲選択開始時に実行する関数を定義する。
|
(6)
|
INPUT・TEXTAREAを除き、範囲選択を行っていない場合は対象外とする。
|
(7)
|
タグがBODY・HTMLの場合は範囲選択不可とする。
|
(8)
|
範囲選択制御に使用する変数をそれぞれ更新する。
|
(9)
|
マウスオーバー時に実行する関数を定義する。
|
(10)
|
カーソルが別要素に到達したら文字列選択をクリアする。
|
(11)
|
範囲選択開始時の要素を設定することで、ハイライトしている範囲を限定する
|
Note
上記のサンプルでは、ブラウザ判定をisIE
関数で行っている。isIE
の実装を以下に示す。
function isIE() { var userAgent = window.navigator.userAgent.toLowerCase(); if (userAgent.match(/trident/)) { return true; } return false; }
Internet Explorerの判定は、ユーザエージェントに特定の文字列(trident
)が含まれるかどうかで行っている。ただし、ユーザエージェントはブラウザのバージョンアップ等で変更される可能性があるため、導入する場合は判定方法を別途検討すること。
Warning
Internet Explorerにおいて、ブロック要素とインライン要素を入れ子にし、インライン要素に範囲選択防止の設定をした場合、ブロック要素上をドラッグ、またはダブルクリックすることでインライン要素をハイライトできる。
HTMLの実装例と範囲選択防止が機能しない例のイメージを以下に示す。
<p> Make particular element of HTML <span class="disable">non-selectable</span>.<br /> Try to select the sentence above without mouse-overing "non-selectable". </p>![]()
図: マウスドラッグによる範囲選択防止を設定した要素のハイライト例
本事象はInternet Explorerがハイライト可の要素を含めることで、不可の要素もハイライトできてしまうことに起因しており、範囲選択防止機能では制御できない。導入する際はブロック要素を範囲選択防止の対象とすること。
5.3. ボタンの活性状態の変更¶
5.3.1. 概要¶
ここでは、jQueryを用いて、ボタンの活性状態を変更する方法を説明する。
例として、チェックボックスをチェックしないと送信ボタンが押せないサンプルを用いて説明する。
利用ライブラリ | サンプル | 参考ページ |
---|---|---|
jQuery | ボタンの活性・非活性 | - |
5.3.2. 利用方法¶
HTMLでは、jQueryと、ボタンの活性状態を制御するために実装したJavaScript(enable-and-disable-button.js)を読み込む。
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="js/enable-and-disable-button.js"></script>
JavaScript(enable-and-disable-button.js)では、チェックボックスのchange
イベントハンドラ内で、
チェック状態に応じて送信ボタンの活性状態を変更する。
// enable-and-disable-button.js
'use strict';
$(function () {
$('#check').on('change', function () {
var $submit = $('#submit');
if (this.checked) {
// (1)
$submit.prop('disabled', false);
} else {
// (2)
$submit.prop('disabled', true);
}
});
});
項番 | 説明 |
---|---|
(1)
|
チェック状態の場合、送信ボタンを活性状態にする。
|
(2)
|
非チェック状態の場合、送信ボタンを非活性状態にする。
|
5.4. 二度押し無効化(二重送信防止)¶
5.4.1. 概要¶
ここでは、jQueryを用いて、ボタンの二度押しを無効化する方法を説明する。
例として、ボタンがクリックされたら処理が完了するまでボタンを非活性化することで、二度押しを無効化するサンプルを用いて説明する。
利用ライブラリ | サンプル | 参考ページ |
---|---|---|
jQuery | 二度押し無効化(ボタン非活性化) | - |
5.4.2. 利用方法¶
HTMLでは、jQueryと、二度押しを無効化するために実装したJavaScript(prevent-continuous-click.js)を読み込む。
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="js/prevent-continuous-click.js"></script>
JavaScript(prevent-continuous-click.js)は、次のように実装する。
// prevent-continuous-click.js
'use strict';
$(function () {
$('#the-form').on('submit', function (e) {
e.preventDefault();
var $form = $(this),
$button = $form.find('[type=submit]');
// (1)
$button.prop('disabled', true);
// (2)
$.ajax({
url : $form.attr('action'),
type : $form.attr('method'),
data : $form.serialize()
})
.then(function () {
console.log("成功")
})
.catch(function() {
console.log("失敗")
})
// (3)
.then(function() {
setTimeout(function () {
// (4)
$button.prop('disabled', false);
}, 2000);
});
});
});
項番 | 説明 |
---|---|
(1)
|
送信ボタンを非活性化する。
|
(2)
|
非同期通信を開始する。
|
(3)
|
通信処理完了後に実行する関数を指定する。
|
(4)
|
送信ボタンを再度活性化する。
Note 本サンプルでは、ボタンの活性状態の変化が分かり易いように、Ajax通信のレスポンスを得てから2秒後にボタンを再活性化している。実際の開発では |
これにより、送信ボタンがクリックされたら直ちに非活性化し、通信処理が完了後に活性状態に戻す動作が実現され、 二度押し無効化が実現できる。
Note
本サンプルでは、通信失敗時や通信中にブラウザの停止ボタンを押下した場合などを考慮していない。
通信の成功(then
)、失敗(catch
)にかかわらず、常にボタンが再活性化する。
実際のシステムでは業務要件に合わせて、通信成功/通信失敗、通信終了時それぞれで、活性化する/常に活性化しないなど処理を実装すること。
then
、catch
の扱いなど、Ajax通信については非同期処理とAjaxを利用した連携も参照すること。
5.4.3. 応用方法¶
5.4.3.1. 同一フォーム内に複数のボタンを設置する場合¶
同一フォーム内に複数のボタンを設置する場合、遷移先情報をHTTPパラメータに設定し実現することが多い。 jQueryのsubmitイベントハンドラ内では、本来HTTPパラメータにセットされるはずだった情報が設定されないため、実装を追加する必要がある。
なお、以下のサンプルは Macchinettaオンライン版 開発ガイドライン と連携することを前提としている。
- HTML
<form:form id="form1" action="${pageContext.request.contextPath}/Doubleclick1/click" modelAttribute="sampleForm" method="POST">
<form:button id="button-aaa" name="aaa">aaa</form:button>
<form:button id="button-bbb" name="bbb">bbb</form:button>
</form:form>
- JavaScript
// (1)
var clickedName = '';
$(function () {
$('#form1').on('submit', function (e) {
var $form = $(this),
$button = $form.find('[type=submit]');
// (2)
var input = $("<input>").attr("type", "hidden")
.attr("name", clickedName);
$form.append($(input));
// (3)
$button.prop('disabled', true);
});
// (4)
$(':button').on('click', function () {
clickedName = $(this).attr('name');
});
});
項番 | 説明 |
---|---|
(1)
|
押下されたボタンのname属性を格納する。
|
(2)
|
複数ボタンを設置した場合、サーバ側のControllerクラスで
@RequestMapping のparam属性による振り分けを設定しているため、本来HTTPパラメータで送信されるはずだったButtonのname属性を手動で設定する必要がある。 |
(3)
|
送信ボタンを非活性化する。
|
(4)
|
押下されたボタンのname属性を取得する関数を定義する。
|
Note
サーバサイドの複数ボタンの設置については Macchinettaオンライン版 開発ガイドライン ( https://macchinetta.github.io/server-guideline/current/ja/の Macchinetta Server Framework (1.x) Development Guideline ) を参照すること。
また、非同期通信用のボタン等で個別に1ボタンずつ二度押しを無効化したい場合はjQueryのclickイベントハンドラ内でボタン毎に制御を実装する。 例えば以下のようなHTMLの場合、
<form id="form1" action="/server-sampleapp-basic/Doubleclick2/click" method="POST">
<input type="button" id="executeService1" value="Execute Function 1" />
<br />
<input type="button" id="executeService2" value="Execute Function 2" />
<br />
<button id="submit" name="submit" type="submit" value="Submit">submit</button>
</form>
非同期通信を実行するボタンに対してはそれぞれ以下のようなJavaScriptを実装する。
// (1)
$('#executeService1').on('click', function () {
$('#executeService1').prop('disabled', true);
$(function () {
$.ajax({
url: contextPath + '/api/v1/dummyServiceForLoading',
type: 'POST',
dataType: 'json',
}).always(function() {
$('#executeService1').prop('disabled', false);
});
});
});
// (2)
$('#executeService2').on('click', function () {
$('#executeService2').prop('disabled', true);
$(function () {
$.ajax({
url: contextPath + '/api/v1/dummyServiceForLoading',
type: 'POST',
dataType: 'json',
}).always(function() {
$('#executeService2').prop('disabled', false);
});
});
});
項番 | 説明 |
---|---|
(1)
|
“Execute Function 1”ボタンを押下した場合に実行する処理を定義する。
|
(2)
|
“Execute Function 2”ボタンを押下した場合に実行する処理を定義する。
|
5.4.3.2. 非同期通信中を表示する場合¶
<button id="executeService">EXECUTE</button> <label id="loadingLabel" style="display: none;">Loading...</label>
$(function() {
// (1)
$('#executeService').click(function() {
// (2)
$('#executeService').prop('disabled', true);
// (3)
$('#loadingLabel').fadeIn();
// (4)
$(function() {
$.ajax({
url: contextPath + '/api/v1/dummyServiceForLoading',
type: 'POST',
dataType: 'json',
})
.then(function () {
console.log("成功")
})
.catch(function() {
console.log("失敗")
})
.then(function() {
// (5)
$('#executeService').prop('disabled', false);
// (6)
$('#loadingLabel').fadeOut();
});
});
});
});
項番 | 説明 |
---|---|
(1)
|
ボタン押下を契機に処理を開始する。
|
(2)
|
ボタンを非活性に変更する。
|
(3)
|
処理中に表示させる文言をフェードインさせる。
|
(4)
|
非同期通信処理を実行する。
|
(5)
|
処理完了後にボタンを再度活性化する。
|
(6)
|
処理中に表示していた文言をフェードアウトさせる。
|
Note
Ajaxを用いたサーバとの非同期通信の詳細については Macchinettaオンライン版 開発ガイドライン ( https://macchinetta.github.io/server-guideline/current/ja/の Macchinetta Server Framework (1.x) Development Guideline ) を参照すること。
5.4.3.3. a要素によるリンクに対して二度押しを無効化する場合¶
a要素を使用したリンクに対して二度押しを無効化する場合、ボタンのようにdisable属性は使用できない。 このため、二度押しを無効化するためのJavaScriptを独自に実装する必要がある。 以下にサンプルを示す。
- HTML
<a id="sample" href="#">sample</a>
- JavaScript
// (1)
var clicked = false;
$(function () {
// (2)
$('a').on('click', function () {
// (3)
if (clicked) {
return false;
};
// (4)
clicked = true;
// (5)
$(function () {
$.ajax({
url: contextPath + '/api/v1/dummyServiceForLoading',
type: 'POST',
dataType: 'json',
}).always(function() {
clicked = false;
});
});
});
});
項番 | 説明 |
---|---|
(1)
|
対象のリンクについて押下の未済を判定するための変数を定義する。
|
(2)
|
a要素が押下された際のイベントハンドラを定義する。
|
(3)
|
押下済みの場合はfalseを返却しリンクを無効化する。
|
(4)
|
押下済みでない場合はフラグを立て押下済みとする。
|
(5)
|
リンク押下時の動作を実装する。
|
5.5. 右クリック無効¶
5.5.2. 利用方法¶
HTMLでは、jQueryと、右クリックを無効化するために実装したJavaScript(disable-right-click.js)を読み込む。
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="js/disable-right-click.js"></script>
JavaScript(disable-right-click.js)では、contextmenu
イベントハンドラ内で、event.preventDefault
を実行することで、ウェブブラウザの標準動作(この例ではコンテキストメニューの表示)を停止している。
// disable-right-click.js
'use strict';
$(function () {
$(document).on('contextmenu', function (event) {
event.preventDefault();
});
});
5.6. 画面要素間の連動¶
5.6.1. 概要¶
画面要素間の連動とは、ある画面要素において、指定されたイベントを検知し、別の画面要素を変更することである。
ここでは、jQueryを用いた画面要素の連動の方法について説明する。
例として、地域区分のドロップダウンリストと都道府県のドロップダウンリストが連動するサンプルを用いる。
利用ライブラリ | サンプル | 参考ページ |
---|---|---|
jQuery | - |
5.6.2. 利用方法¶
HTMLでは、jQueryと、画面要素間の連動を制御するために実装したJavaScript(work-with-element.js)を読み込む。
連動させる要素である地域区分と都道府県のドロップダウンリストにはidを設定する。
<h3>地域区分:</h3>
<select id="region">
<option>-- 地域区分 --</option>
<option value="hokkaido">北海道</option>
<option value="tohoku">東北</option>
<option value="kanto">関東</option>
<option value="chubu">中部</option>
<option value="kinki">近畿</option>
<option value="chugoku">中国</option>
<option value="shikoku">四国</option>
<option value="kyushu">九州・沖縄</option>
</select>
<br>
<h3>都道府県:</h3>
<select id="prefecture">
<option>-- 都道府県 --</option>
</select>
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="js/work-with-element.js"></script>
JavaScript(work-with-element.js)では、onメソッドを使って地域区分ドロップダウンリストにchangeイベントが発生した際に実行する関数を設定する。
設定した関数の中で、選択された地域区分の情報をキーに都道府県リストを取得し、そのリストを都道府県ドロップダウンリストに追加する。
// work-with-element.js
'use strict';
$(function () {
// (1)
var regions = $('#region');
var prefectures = $('#prefecture');
// (2)
regions.on('change', function () {
// (3)
var prefecture = prop.data[regions.val()];
// (4)
prefectures.empty();
// (5)
if (prefecture instanceof Array) {
for (var i = 0, len = prefecture.length; i < len; i++) {
var option = $('<option>').text(prefecture[i].text)
.val(prefecture[i].value);
prefectures.append(option);
}
}
});
});
// (6)
var prop = {
'data' : {
'hokkaido' : [
{'text': '道央', 'value': 'douou'},
{'text': '道北', 'value': 'douhoku'},
{'text': '道東', 'value': 'doutou'},
{'text': '道南', 'value': 'dounan'}
],
// 省略
'kyushu' : [
{'text': '福岡', 'value': 'fukuoka'},
{'text': '佐賀', 'value': 'saga'},
{'text': '長崎', 'value': 'nagasaki'},
{'text': '熊本', 'value': 'kumamoto'},
{'text': '大分', 'value': 'oita'},
{'text': '宮崎', 'value': 'miyazaki'},
{'text': '鹿児島', 'value': 'kagoshima'},
{'text': '沖縄', 'value': 'okinawa'}
]
}
};
項番 | 説明 |
---|---|
(1)
|
連動させる要素を取得する。
|
(2)
|
地域区分が選択されたときに実行する関数を定義する。
|
(3)
|
選択された地域区分の情報をキーに都道府県リストを取得する。prop.dataには、地域ごとに分けられた都道府県オブジェクトの配列が定義されている。
|
(4)
|
都道府県ドロップダウンリストを一旦空にする。
|
(5)
|
都道府県ドロップダウンリストに取得した都道府県リストを追加する。
|
(6)
|
地域ごとに分けられた都道府県オブジェクトの配列。
|
5.6.3. 応用方法¶
画面要素間の連動を実現する際は、リストをJavaScriptファイルにハードコーディングするのではなく、サーバもしくは外部ファイルに持たせて取得するケースが考えられる。
ここでは、非同期通信で取得したリストを画面要素に反映する方法を紹介する。以下に例を示す。
HTMLは同様のため割愛する。(ただし、本サンプルでは非同期通信失敗時の挙動も確認できるよう、「非同期通信挙動選択」のラジオボタンを配置している。)
- JavaScript
// work-with-element-ajax.js
'use strict';
$(function () {
var regions = $('#region');
var prefectures = $('#prefecture');
regions.on('change', function () {
// (1)
var callback = function (data) {
var prefecture = data[regions.val()];
prefectures.empty();
if (prefecture instanceof Array) {
for (var i = 0, len = prefecture.length; i < len; i++) {
var option = $('<option>').text(prefecture[i].text)
.val(prefecture[i].value);
prefectures.append(option);
}
}
};
// (2)
$.ajax({
'type' : 'GET',
'url' : url,
'dataType' : 'json'
})
.then(callback)
.catch(function() {
$('#message-area').append('<p>エラー発生 : 通信に失敗しました。</p>');
prefectures.empty();
});
});
});
項番 | 説明 |
---|---|
(1)
|
選択された地域区分の情報をキーに都道府県リストを設定する関数。引数dataには非同期通信で取得した都道府県リストを受け取る。
|
(2)
|
非同期通信を実行する。非同期通信成功後にthenのコールバックを実行する。
|
上記のサンプルのように、ajaxを利用することでJavaScriptファイルにハードコーディングせず、非同期通信でリストを取得し画面要素に反映できる。ajaxの詳細な利用方法については Ajaxを利用した連携 を参照すること。
上記では省略しているが、本サンプルでは非同期通信失敗後の挙動も確認できるよう実装しているので参考にしてほしい。
5.7. フォーマット変換・文字種変換¶
5.7.1. 概要¶
利用ライブラリ | サンプル | 参考ページ |
---|---|---|
Moment.js | ||
- | 特定文字の全角半角変換 | - |
5.7.2. 利用方法¶
5.7.2.1. 日付フォーマット変換¶
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="../lib/vendor/moment/2.22.2/moment.min.js"></script>
<script src="js/check-date.js"></script>
// convert-date.js
'use strict';
$(function () {
$('#date').on({
// (1)
'focus' : function () {
var date = $('#date').val();
if (date === '') {
return;
}
// (2)
$('#date').val(moment(date, 'YYYY/MM/DD', true).format('YYYYMMDD'));
},
// (3)
'blur' : function () {
var date = $('#date').val();
if (date === '' || moment(date, 'YYYY/MM/DD', true).isValid()) {
return;
}
// (4)
$('#date').val(moment(date, 'YYYYMMDD', true).format('YYYY/MM/DD'));
}
});
});
項番 | 説明 |
---|---|
(1)
|
フォーカス時に発生するイベントを定義する。
|
(2)
|
スラッシュなしのフォーマットに変換する。
|
(3)
|
フォーカスアウト時に発生するイベントを定義する。
|
(4)
|
スラッシュありのフォーマットに変換する。
|
moment
メソッドの第1引数に日付(文字列)、第2引数にフォーマットを設定すると、フォーマットに従ってパースする。format
メソッドにフォーマットを設定すると、指定したフォーマットに変換した日付を取得できる。Note
moment
の第3引数にtrue
を設定すると、Strictモードで動作する。Strictモードは、入力値がフォーマットと一致していることを厳密にチェックし、一致する場合のみ有効な日付としてパースする。
第3引数を省略、またはfalse
を設定した場合、Forgivingモードで動作する。Forgivingモードは、入力値がフォーマットとある程度異なっても有効な日付としてパースする。
各モードの挙動の差異について、以下に例を示す。
// (1) // (1-a) moment('2015-01-01', 'YYYY/MM/DD', false).format('YYYYMMDD'); // (1-b) moment('2015-01-01', 'YYYY/MM/DD', true).format('YYYYMMDD'); // (2) // (2-a) moment('2015/01/31 is Date', 'YYYY/MM/DD', false).format('YYYYMMDD'); // (2-b) moment('2015/01/31 is Date', 'YYYY/MM/DD', true).format('YYYYMMDD');
項番 説明 (1) 入力値(2015-01-01)とフォーマット(YYYY/MM/DD)が異なる場合。 (1-a) Forgivingモードの場合では返却値が20150101
となる。 (1-b) StrictモードではInvalid date
となる。 (2) 入力値(2015/01/31 is Date)に日付以外が含まれる場合。 (2-a) Forgivingモードの場合では返却値が20150131
となる。 (2-b) StrictモードではInvalid date
となる。
また、Forgivingモードはフォーマットを厳密にチェックしないため、入力値が誤って変換される可能性がある。
// (1) moment('01/12/2016', 'YYYY/MM/DD', false).format('YYYYMMDD');
項番 説明 (1) 入力値(01/12/2016)とフォーマット(YYYY/MM/DD)が異なるが、Forgivingモードでは20011220
として解釈される。
このように、Forgivingモードは故障に繋がる可能性があるため、Strictモードを利用することを推奨する。
Warning
入力値に誤りがある場合、入力欄に「Invalid date」が出力される。メッセージの文言や出力位置は変更できないため、入力値の妥当性を事前にチェックし、エラーの場合はメッセージを出力するよう実装すること。チェック方法は日付妥当性チェックに記述する。
5.7.2.2. 日付妥当性チェック¶
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="../lib/vendor/moment/2.22.2/moment.min.js"></script>
<script src="js/check-date.js"></script>
blur
イベントが発生した際、入力値の妥当性をチェックする。moment
メソッドの引数に、入力値、フォーマットとStrictモードで実行するためのtrue
を設定し、isValid
メソッドを実行する。// check-date.js
'use strict';
$(function () {
$('#date').on({
// (1)
'blur' : function () {
var date = $('#date').val();
// (2)
var result = moment(date, 'YYYY/MM/DD', true).isValid();
$('#date-area > span').remove();
if (result) {
$('#date-area').append('<span>入力値は正常です。</span>');
} else {
$('#date-area').append('<span>入力値に誤りがあります。</span>');
}
}
});
});
項番 | 説明 |
---|---|
(1)
|
フォーカスアウト時に発生するイベントを定義する。
|
(2)
|
入力値の妥当性をチェックする。
|
moment
メソッドは、日付をパースする際、フォーマットに合致していること、日付が実在することをチェックする。isValid
メソッドを実行すると、チェック結果を確認できる。パースに成功した場合はtrue
、失敗した場合はfalse
が返却される。Note
複数のフォーマットを許容する場合、以下のように配列を設定すればよい。
var result = moment(date, ['YYYY/MM/DD','YYYYMMDD'], true).isValid();
Note
日付フォーマット変換のサンプルに本節の妥当性チェックを組み合わせた実装例を以下に示す。
// (1) 'blur' : function () { var date = $('#date').val(); // (2) var result = moment(date, 'YYYYMMDD', true).isValid(); if (!result) { $('#form-area').append('<span>入力値に誤りがあります。</span>'); return false; } // (3) $('#date').val(moment(date, 'YYYYMMDD', true).format('YYYY/MM/DD')); }
項番 説明 (1) フォーカスアウト時に発生するイベントを定義する。 (2) 入力値の妥当性をチェックする。 (3) スラッシュありのフォーマットに変換する。
5.7.2.3. 時刻フォーマット変換¶
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="../lib/vendor/moment/2.22.2/moment.min.js"></script>
<script src="js/convert-time.js"></script>
// convert-time.js
'use strict';
$(function () {
$('#time').on({
// (1)
'focus' : function () {
var time = $('#time').val();
if (time === '') {
return;
}
// (2)
$('#time').val(moment(time, 'HH:mm:ss', true).format('HHmmss'));
},
// (3)
'blur' : function () {
var time = $('#time').val();
if (time === '' || moment(time, 'HH:mm:ss', true).isValid()) {
return;
}
// (4)
$('#time').val(moment(time, 'HHmmss', true).format('HH:mm:ss'));
}
});
});
項番 | 説明 |
---|---|
(1)
|
フォーカス時に発生するイベントを定義する。
|
(2)
|
コロンなしのフォーマットに変換する。
|
(3)
|
フォーカスアウト時に発生するイベントを定義する。
|
(4)
|
コロンありのフォーマットに変換する。
|
moment
メソッドの第1引数に時刻(文字列)、第2引数にフォーマットを設定すると、フォーマットに従ってパースする。format
メソッドにフォーマットを設定すると、指定したフォーマットに変換した時刻を取得できる。Warning
moment
のパラメーターに不正な値を設定した場合、入力欄に「Invalid date」や想定外の値が出力される。入力値の妥当性を事前にチェックし、エラーの場合はメッセージを出力するよう実装すること。日付妥当性チェックと同様の方法でチェックできる。
5.7.2.4. 特定文字の全角半角変換¶
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="js/convert-zenkaku-hankaku.js"></script>
blur
イベントが発生した際、独自に実装したconvertStyle
関数を実行する。コードを区切って説明する。style
オブジェクトに変換対象の全角文字・半角文字を定義する。var style = {
'zenkaku' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 (){}[]<>=+‐-*/|_?,.¥@^;:!#$%&',
'hankaku' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 (){}[]<>=+--*/|_?,.\\@^;:!#$%&'
};
convertStyle
関数は、input
の文字数分ループする。fromStyle
に格納された文字に該当する場合、toStyle
の文字に置換する。// (1)
var convertStyle = function (input, type) {
var fromStyle;
var toStyle;
var output = [];
switch (type) {
case 'zenkaku':
// (2)
fromStyle = style['hankaku'];
// (3)
toStyle = style['zenkaku'];
break;
default:
// (4)
fromStyle = style['zenkaku'];
// (5)
toStyle = style['hankaku'];
break;
}
var pos;
for (var i = 0, len = input.length; i < len; i++) {
// (6)
pos = fromStyle.indexOf(input.charAt(i));
if (pos < 0) {
// (7)
output[i] = input.charAt(i);
} else {
// (8)
output[i] = toStyle.charAt(pos);
}
}
return output.join('');
};
項番 | 説明 |
---|---|
(1)
|
特定文字の全角半角変換用の関数を定義する。
第1引数に変換対象の文字列を指定し、第2引数に変換形式(半角から全角に変換する場合は「zenkaku」、全角から半角に変換する場合は「hankaku」)を指定する。
|
(2)
|
typeが「zenkaku」の場合、変換前の文字列に半角文字列を格納する。
|
(3)
|
変換後の文字列に全角文字列を格納する。
|
(4)
|
typeが「hankaku」の場合、変換前の文字列に全角文字列を格納する。
|
(5)
|
変換後の文字列に半角文字列を格納する。
|
(6)
|
入力値の位置を取得する。
|
(7)
|
該当しない場合、入力値を格納する。
|
(8)
|
該当する場合、’toStyle’の文字を格納する。
|
convertStyle
関数を呼び出す。第2引数に変換後の文字種(「zenkaku」・「hankaku」)を指定する。 $('#zenkaku-string').on({
// (9)
'blur' : function () {
var st = $('#zenkaku-string').val();
// (10)
$('#zenkaku-string').val(convertStyle(st, 'zenkaku'));
}
});
$('#hankaku-string').on({
'blur' : function () {
var st = $('#hankaku-string').val();
// (11)
$('#hankaku-string').val(convertStyle(st, 'hankaku'));
}
});
});
項番 | 説明 |
---|---|
(9)
|
フォーカスアウト時に発生するイベントを定義する。
|
(10)
|
半角文字を全角に変換する。
|
(11)
|
全角文字を半角に変換する。
|
Note
本実装例は、styleオブジェクトのzenkakuとhankakuに、変換対象の文字が対になるように定義する。変換時は対象文字の定義位置を取得し、相対する文字種から同じ位置に定義されている文字を取得することで変換処理を実現している。
実装例のstyleオブジェクトにない文字を変換したい場合、styleオブジェクトの定義内容を変更すればよいが、定義する文字や順序に誤りが無いよう注意すること。
Note
「英字」を大文字、または小文字のみに変換する場合、JavaScriptのtoUpperCase
・toLowerCase
で入力値を変換すればよい。
5.8. 入力値チェック¶
5.8.1. 概要¶
利用ライブラリ | サンプル | 参考ページ |
---|---|---|
Parsley | Parsley - The ultimate documentation |
Note
クライアントサイドでの入力値チェックは、入力値が受け付けられるかどうかをサーバにリクエストすることなく即座に確認できるため、ユーザビリティの向上に寄与する。
ただし、クライアントから送信されるパラメータは容易に改ざんできるため、サーバサイドでの入力値チェックの代替にはならない。業務上のデータ不整合を防ぐためには必ずサーバサイドでのチェックを実施すること。
5.8.2. 利用方法¶
5.8.2.1. 基本的な使用方法¶
HTMLでは、次の順番でJavaScriptを読み込む。
- jQuery
- Parsley本体
- Parsley拡張プラグイン (日付形式検証などの、拡張プラグインとして提供されている機能を使用する場合のみ)
- Parsleyメッセージ定義ファイル (指定しない場合メッセージは英語で出力される。)
- 独自実装したJavaScript
<script src="../lib/vendor/jquery/3.3.1/jquery.min.js"></script>
<script src="../lib/vendor/parsleyjs/2.8.1/parsley.min.js"></script>
<script src="../lib/vendor/parsleyjs/2.8.1/extra/validator/dateiso.js"></script>
<script src="../lib/vendor/parsleyjs/2.8.1/i18n/ja.js"></script>
<script src="js/validation.js"></script>
検証ルールを適用するためには、対象のinput
に対してdata-parsley-検証ルール名
という属性を指定する。次の例は、年齢フィールドが「入力必須」「正の整数値」「20以上」であることを検証する例である。
<label for="age">年齢</label><br>
<input id="age" name="age" type="text"
data-parsley-type="digits"
data-parsley-min="20" data-parsley-min-message="未成年は登録できません。">
検証エラー時のメッセージは、エラーとなった検証ルールのデフォルトメッセージが表示される ( 設定方法は デフォルトエラーメッセージ定義と国際化対応 を参照) 。ただし、上の例にあるdata-parsley-min-message="未成年は登録できません。"
のように、検証ルール属性-message
属性を用いることで、特定の検証ルールのみメッセージを変更できる。
その他の検証ルールとして、以下が使用できる。
属性 | 用途 |
---|---|
data-parsley-required="true" |
入力必須であることを検証する。 |
data-parsley-type="digits" |
正の整数値であることを検証する。 |
data-parsley-type="email" |
Eメール形式であることを検証する。 |
data-parsley-min="n" (n は正の整数)
|
n 以上の数値であることを検証する。 |
data-parsley-max="n" (n は正の整数)
|
n 以下の数値であることを検証する。 |
data-parsley-length="[m, n]" (m, n は正の整数)
|
m 文字以上 n 文字以下であることを検証する。 |
data-parsley-equalto="セレクタ文字列" |
セレクタで指定した要素の値と同じ値であることを検証する。 |
検証ルールの完全な一覧は Validators list を参照すること。
JavaScript(validation.js)では、 入力値チェック対象となるフォーム要素をjQueryでセレクトし、parsley
メソッドを実行する。
parsley
メソッドの引数には、次のようにオプションを指定するためのオブジェクトを指定できる。
// validation.js
'use strict';
$(function () {
// (1)
$('#form').parsley({
// (2)
inputs: 'input, textarea, select',
// (3)
excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]',
// (4)
errorClass: 'has-error',
// (5)
successClass: '',
// (6)
classHandler: function (ParsleyField) {
// (7)
return ParsleyField.$element.parent();
},
// (8)
errorsContainer: function (ParsleyField) {
// (9)
},
// (10)
errorsWrapper: '<ul class="parsley-errors-list"></ul>',
// (11)
errorTemplate: '<li></li>'
});
});
項番 | 説明 |
---|---|
(1)
|
バリデーションを有効化する。
|
(2)
|
検証対象の要素を指定する。(セレクタ文字列、またはjQueryオブジェクト)
|
(3)
|
検証対象から除外する要素を指定する。(セレクタ文字列、またはjQueryオブジェクト)
|
(4)
|
検証エラー時に付与するクラス名を指定する。
|
(5)
|
検証成功時に付与するクラス名を指定する。
|
(6)
|
検証結果に応じてクラスを付与する要素を指定する。(セレクタ文字列、jQueryオブジェクト、またはそれらを返す関数)
|
(7)
|
検証エラーが起きた input要素の親要素に
has-error クラスを付与する。 |
(8)
|
エラーメッセージを追加する要素を指定する。(文字列、jQueryオブジェクト、またはそれらを返す関数)
|
(9)
|
エラーメッセージの表示位置を指定する。実装しない場合(オプション未指定時)や
undefined を返すと、input要素の次に作られる。 |
(10)
|
エラーメッセージの親要素に用いるHTML文字列を指定する。
|
(11)
|
エラーメッセージ表示に用いるHTML文字列を指定する。
|
これらのオプションを使用することで、エラー時のメッセージ表示位置やスタイルの変更などを柔軟にカスタマイズすることができる。
5.8.2.2. デフォルトエラーメッセージ定義と国際化対応¶
Parsleyの出力するデフォルトのエラーメッセージは英語であるが、他の各言語についてもデフォルトのエラーメッセージ定義が提供されている (i18nディレクトリ配下)。また、それらのファイル内のメッセージ定義を任意の内容に書き換えることができる。日本語のメッセージを変更する場合は、i18n/ja.js に定義されているメッセージ部分を直接書き換えればよい。
デフォルトのエラーメッセージを変更したい場合は、次のように Parsley 本体の後に読み込む。
<script src="jquery.js"></script>
<script src="parsley.min.js"></script>
<script src="i18n/ja.js"></script>
複数のロケールに対応する場合には、次のように複数の言語のメッセージ定義を読み込んだ後、Parsley.setLocale
メソッドを用いて適用するロケールを指定する。
<script src="jquery.js"></script>
<script src="parsley.min.js"></script>
<script src="i18n/fr.js"></script>
<script src="i18n/ja.js"></script>
<script type="text/javascript">
var locale = (navigator.language || navigator.userLanguage).substring(0, 2);
try {
window.Parsley.setLocale(locale);
} catch (e) {
window.Parsley.setLocale('en');
}
</script>
この例は、Parsley.setLocale
メソッドで指定するロケールをウェブブラウザの設定言語から取得して適用する例である。
この例では ‘en’, ‘fr’, ‘ja’ が有効となり、ウェブブラウザから取得したロケールから選択される。
5.8.2.3. カスタムバリデータの実装方法¶
独自の検証ルールとメッセージを追加するためには、Parsley本体の読み込み後に、Parsley.addValidator
メソッドを使用する。
次の例は、指定した数値の倍数であることを検証する独自の検証ルール multipleof
の実装および使用例である。
HTMLでは以下のようなinput要素を設置する。
<input type="text" data-parsley-multipleof="3">
独自の検証ルール名 multipleof
に合わせた data-parsley-multipleof
属性に値を指定する。
Javascriptでは以下のように実装する。
// (1)
Parsley.addValidator('multipleof', {
// (2)
requirementType: 'number',
// (3)
validateNumber: function (value, requirement) {
return 0 === value % requirement;
},
// (4)
messages: {
en: 'This value should be a multiple of %s',
ja: '%s の倍数である必要があります。'
}
});
項番 | 説明 |
---|---|
(1)
|
第1引数に検証ルール名、第2引数に検証ルールのオブジェクトを指定した
addValidator メソッドを使用する。 |
(2)
|
要求パラメータの型。カスタムバリデータが期待している要求パラメータ(上記例では
data-parsley-multipleof="3" )の型を指定する。
string 、integer 、number 、date 、regexp 、boolean が用意されており、これらの配列も利用できる。 |
(3)
|
検証を実行する関数で、入力値が期待する型に合わせて、
validateString 、validateNumber 、validateDate 、validateMultiple の中から少なくとも1つを指定する必要がある。
2つの引数として、対象のinput 要素の値と、オプション (data-parsley-multipleof="3" のように使用した場合は3 ) を受け取る。成功時はtrue 、失敗時はfalse を返すように実装する。 |
(4)
|
検証エラー時に表示されるメッセージを設定する。
keyにロケール、valueにメッセージ本文となるObjectとなるよう設定する。メッセージ本文には
%s プレースホルダを用いることでオプション値を埋め込むことができる。 |
Note
より高度な入力値チェックの例として、サーバ通信を伴う入力値チェックがある。具体例の一つとして、ユーザ登録フォームの「ユーザID」が利用可能かどうかを検証するためサーバに問い合わせるといったケースが考えられる。
このような機能も、カスタムバリデータを実装することで実現できる。
Parsley.addValidator('registerable', { requirementType: 'string', // (1) validateString: function (value) { var status = $.ajax({ url: '/userid_available', // (2) data: 'id=' + value, // (3) async: false }).status; // (4) return status !== 409; }, messages: { en: 'this USER ID is already used' } });
項番 説明 (1) 登録可能なユーザIDかどうかを検証する関数を定義する。 (2) パラメータ名をid
とし、入力値を設定する。 (3)async
オプションにfalse
を設定し同期処理とする。 (4) ステータスコード 409 (Conflict) の場合は登録不可
検証を実行する関数内で結果を返却するため、$.ajax
のasync
オプションをfalse
にすることで同期的に処理する必要がある。
なお、非同期で検証を行えるカスタムバリデータを作成する専用APIとして、Parsley.addAsyncValidator
メソッドがある (参考)。
ただし、これを用いると、検証時に送信されるパラメータが name属性値=value
で固定化され、変形することができない (上のサンプルコード中の data: 'id=' + value
のように、パラメータ名を”id”に固定するといったことができない)。
よって、サーバのAPI仕様に合わせてパラメータを変形する必要がある場合はParsley.addValidator
メソッドを、必要ない場合はParsley.addAsyncValidator
メソッドを用いるとよい。