jQuery 插件 - 簡易欄位檢核訊息顯示
6 |
前篇文章提到有個 blur() + alert() IE Only 網頁在 Edge/Chrome 會發生 alert 無窮迴圈的悲劇,讓我心生寫個簡單通用程式庫解決這類需求的念頭。規格如下:
- 希望搭配原有檢核邏輯,程式不要大改,換掉原本 alert 訊息顯示就好
- 要能明確指出檢核失敗欄位所在位置
- 支援將焦點移回無效欄位,強迫輸入正確才可離開的模式
- 力求簡便易用,希望單一 .js 搞定,不需額外載入 .css
- 能支援 IE11 更佳
這樣的需求用純 JavaScript 理論上也寫得出來,但我決定寫成 jQuery 插件,節省程式碼跟腦細胞。
直接看結果,車牌欄位採強迫校正,會加上遮罩阻止操作,並將焦點移回車牌欄位;其餘則是在欄位前方標註訊息,點擊可移除。標註位置以欄位左上為原則,則欄位位於頁頂空間不足,則標於左下角。
呼叫方式如下:
<table>
<tr>
<td>車牌1</td><td><input id="c1" /></td>
<td>欄位X</td><td><input id="x1" /></td>
</tr>
<tr>
<td>說明</td>
<td colspan="3">
<textarea id="t1" style="width: 100%" rows="4"></textarea>
</td>
</tr>
<tr>
<td>欄位A</td><td><input id="f1" /></td>
<td>欄位B</td><td><input id="f2" /></td>
</tr>
</table>
<button onclick="validate()">測試檢核</button>
<script>
$('#c1').blur(function () {
var inp = $(this);
if (!/^[-0-9A-Z]{5,7}$/.exec(inp.val())) {
inp.showInvalidMessageTag("車牌格式有誤,請更正!", { forceChange: true });
}
});
function validate() {
$('input,textarea').not('#c1').each(function () {
var inp = $(this);
if (!inp.val())
inp.showInvalidMessageTag("欄位不可空白");
});
}
</script>
為擴大相容性,用 IE11 嘛也通。
[2022-06-29 更新]
採納讀者 78 建議,加上 10 秒訊息自動消失,預設開啟,但可透過參數 inp.showInvalidMessageTag("檢核有誤!", { autoHide: false })
停用,仿效 NOTY 顯示倒數進度,滑鼠滑過時會停止倒數,回歸點選關閉:
jQuery 插件完整程式碼如下,也有線上展示,但這個版本仍屬雛型,還沒上戰爭歷練過,歡迎大家試玩並提供建議:
jQuery.fn.showInvalidMessageTag = function (msg, options) {
//ver 1.1 add auto clear
options = $.extend({ autoHide: true, forceChange: false }, options);
var forceChange = options.forceChange;
if (forceChange) options.autoHide = false;
//add style block
var styleBlock = $('#invld-msg-styles');
if (!styleBlock.length) {
$("\
<style id='invld-msg-styles'>\
.ivmt-tag { font-size:10pt; position: absolute; cursor: pointer; z-index: 99999; opacity: 0.85 }\
.ivmt-tag .arw { color: #e53939; font-size: 12px; padding-left: 2px }\
.ivmt-tag .msg { background-color: #e53939; color: white; padding: 4px 12px; width: auto; margin-top: -4px; border-radius: 3px}\
.ivmt-tag .prog { width: 100%; background-color: yellow; height: 2px; opacity: 0.5; }\
.ivmt-mask-layer { position: absolute; z-index: 65535; opacity: 0.3; background-color: #888; top: 0; left: 0; width: 100vw }\
</style>").prependTo('body');
}
var elem = $(this);
var maskLayer = $('.ivmt-mask-layer');
if (forceChange && maskLayer.length) return;
var pos = elem.offset();
var bgc = '#e53939';
var tag = $('<div class="ivmt-tag" style="visibility:hidden;"><div class="arw">▲</div><div class="msg"><div class="text"></div></div>');
tag.find('.msg .text').text(msg);
if (options.autoHide) tag.find('.msg').append('<div class="prog"></div>');
tag.appendTo('body');
var tagPos = pos.top < tag.height() ? 'bottom' : 'top';
var top = pos.top + elem.height();
if (tagPos == 'top') {
top = pos.top - tag.height() + 4;
var msgBlock = tag.find('.msg');
msgBlock.css('marginTop', '0');
tag.find('.arw').text('▼').css('marginTop', '-5px').before(msgBlock);
}
if (forceChange) {
maskLayer = $('<div class=ivmt-mask-layer></div>');
maskLayer.css({ height: Math.max(window.innerHeight, document.body.scrollHeight) + 'px' }).appendTo('body');
}
if (!forceChange && options.autoHide) {
var count = 100;
var hnd = setInterval(function() {
count--;
tag.find('.prog').css('width', count + '%');
if (count <= 0) {
clearInterval(hnd);
tag.remove();
}
}, 100);
tag.hover(function() {
clearInterval(hnd);
tag.find('.prog').remove();
});
}
tag.css({ visibility: 'visible', top: top + 'px', left: pos.left + 'px' })
.add(maskLayer).click(function () {
tag.remove();
if (forceChange) {
elem.focus();
maskLayer.remove();
}
});
}
A simple jQuery plugin to show invalid message on the input fields.
Comments
# by 78
無能者一點小建議 覺得讓警示過段時間消失比較好 一個個點警示有點麻煩
# by Jeffrey
to 78,好建議! 改了一個版本,加入倒數自動關閉功能。
# by Toolman
黑大好 關於 css 的部分,想請問 1. css 獨立成一份 css file 會不會比較好呢? 2. progress 倒數計時使用 setInterval 那邊,用 window.requestAnimationFrame 會不會比較好? 理論上動畫會比較順暢一點
# by Jeffrey
to Toolman, 獨立 css 檔是較正統做法,但缺點是每次應用時要多部署一個檔,網頁要多加一行 <link href=...>,值不值得因人而異。有人不介意多費點力氣去維持嚴謹,我則偏好「程式庫用起來愈方便愈好」,大家取捨的點不同。如果要想自訂 css,options 可再增加一個 custCssUrl 動態載入。 requestAnimationFrame() 我不太熟,要怎麼應用還需要研究一下。
# by Toolman
其實用法和 setInterval 差不多,都是丟個 callback 進去,只是 requestAnimationFrame 的 callback 還要記得再呼叫 requestAnimationFrame 情境上,只要是想用 js 做動畫的話,requestAnimationFrame 會幫忙處理跟瀏覽器 FPS 相關的東西 如果用 setInterval,很容易發生 fps 不夠高,導致動畫看起來卡卡的 這幾篇 stackover 上的討論還不錯 Why is requestAnimationFrame better than setInterval or setTimeout https://stackoverflow.com/questions/38709923/why-is-requestanimationframe-better-than-setinterval-or-settimeout requestAnimationFrame loop not correct FPS https://stackoverflow.com/questions/43379640/requestanimationframe-loop-not-correct-fps/43381828#43381828
# by Jeffrey
to Toolman, 感謝分享。