通常、設定が変更されたときに設定を検証する必要があります。これは、onchange
属性(および対応するchange
イベント) が適しているものです。
<preference name="preference.name" onchange="validate(this);"/>
このイベントは、設定値が変更された後に発生します。2 つの欠点があります。
- 新しい設定値がすでに保存されている場合、
instantApply
検証して拒否するには遅すぎます。
- テキスト フィールドの場合、新しい文字が入力されるたびに設定が保存されます。ユーザーがまだ入力している間に検証の失敗を報告すると、これは見苦しくなります。
最初の問題は、実際の入力フィールドの変更イベントをインターセプトすることで解決できます。たとえば、テキスト フィールドの場合は次のようにします。
<input preference="preference.name"
oninput="if (!validate(this)) event.stopPropagation();"
onchange="if (!validate(this)) { event.stopPropagation(); this.focus(); }"/>
そのため、正しく検証されない変更は<prefpane>
要素に反映されず、保存されません。リッスンするイベントはinput
、change
テキスト フィールド、command
ボタンとチェックボックス、select
要素<colorpicker>
です。
2番目の問題はトリッキーです。入力が発生したときに入力を検証する必要がありますが、メッセージをすぐに表示するのは悪い UI です。最良の解決策は、最初は各入力フィールドがまだ「進行中」であると想定することだと思います。blur
フィールドで最初にイベントが表示されたときにのみ、値が完了したというフラグを設定します。このとき、必要に応じて検証メッセージを表示できます (モーダル プロンプトではなく、設定ページに表示される赤いテキストが理想的です)。
したがって、最終的なソリューションがどのように見えるかを示すために(テストされていないコードですが、過去にそのようなものを使用しました):
<description id="error" hidden="true">Invalid preference value</description>
<input preference="preference.name"
_errorText="error"
onblur="validate(event);"
oninput="validate(event);"
onchange="validate(event);/>
<script>
function validate(event)
{
// Perform actual validation
var field = event.target;
var valid = isValid(field);
// If this is the blur event then the element is no longer "in progress"
if (event.type == "blur")
{
field._inputDone = true;
if (!valid)
field.focus();
}
// Prevent preferences changing to invalid value
if (!valid)
event.stopPropagation();
// Show or hide error text
var errorText = document.getElementById(field.getAttribute("_errorText"));
errorText.hidden = valid || !field._inputDone;
}
</script>