[英]Is there a better way to do this than using an IIFE?
我正在嘗試為 forms 創建一個模板,該模板檢查 HTML 元素上的data-regex
自定義參數,然后從中創建一個正則表達式以用於驗證輸入。 這是我到目前為止所擁有的。
var textInputs = document.getElementsByClassName("form__text"); for (var i = 0; i < textInputs.length; ++i) { if (textInputs[i].getElementsByTagName("input")[0].type == "email") { (function() { var pattern = /.*[a-zA-Z0-9]+.*@.*\.[a-zA-Z]+/; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); })(); } else if (textInputs[i].getElementsByTagName("input")[0].type == "text") { (function() { var patternStr = textInputs[i].getAttribute("data-regex"); var pattern = patternStr? new RegExp(patternStr): null; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); })(); } } function checkText(pattern, e) { if (pattern && e.target.value.search(pattern) == -1) { e.target.parentElement.classList.add("form__text--error"); } else { e.target.parentElement.classList.remove("form__text--error"); } }
* { margin: 0; padding: 0; box-sizing: border-box; }.form { margin: 10px; }.form.form__text { position: relative; margin: 2rem 0 4rem 0; display: block; }.form.form__text__label { position: absolute; font-size: 1.4rem; padding: 10px; opacity: 0.5; top: 50%; left: 0; pointer-events: none; transition: all 0.2s ease-out; transform: translateY(-50%); }.form.form__text__error-label { color: red; opacity: 0; transition: all 0.2s ease-out; position: absolute; top: 110%; left: 0; }.form.form__text input[type=text], .form.form__text input[type=email] { padding: 10px; width: 100%; border: 1px solid #ccc; border-radius: 5px; transition: all 0.2s ease-out; }.form.form__text input[type=text]:focus ~.form__text__label, .form.form__text input[type=text]:not(:placeholder-shown) ~.form__text__label, .form.form__text input[type=email]:focus ~.form__text__label, .form.form__text input[type=email]:not(:placeholder-shown) ~.form__text__label { transform: translateX(-15px) translateY(-125%) scale(0.75); opacity: 1; }.form.form__text input[type=text]:focus, .form.form__text input[type=email]:focus { outline: none; background: rgba(122, 217, 255, 0.075); }.form.form__text--error.form__text__label { color: red; }.form.form__text--error.form__text__error-label { opacity: 1; }.form.form__text--error input[type=text], .form.form__text--error input[type=email] { border: 1px solid red; }.form.form__text--error input[type=text]:focus, .form.form__text--error input[type=email]:focus { background: rgba(255, 0, 0, 0.05); }
<form class="form"> <label class="form__text"> <input type="email" id="email" name="email" placeholder=" " /> <span class="form__text__label">Email</span> <span class="form__text__error-label">Invalid Email</label> </label> <label class="form__text" data-regex="[a-zA-z ]{4,}"> <input type="text" id="name" name="name" placeholder=" " /> <span class="form__text__label">Name</span> <span class="form__text__error-label">Invalid Name</span> </label> <label class="form__text"> <input type="text" id="random" name="random" placeholder=" " /> <span class="form__text__label">Random Fact</span> </label> </form>
我不得不將兩個addEventListener
塊包裝在 IIFE 中,因為如果沒有,只要觸發回調,模式變量就會被覆蓋。 我很好奇是否有更清潔的方法來做到這一點。 我假設創建正則表達式對象需要一些費用,這就是為什么我試圖在回調之外創建它們。 提前致謝。
問題是pattern
將是整個 function 的共享變量名。 然后稍后當事件偵聽器觸發時,它將拾取pattern
的最新版本而不是當前版本。 這是一個經典的問題。
隨着let
和const
的引入,您可以在塊 scope 中擁有變量,因此解決方案非常簡單 - 將任何var
更改為let
或const
。
const textInputs = document.getElementsByClassName("form__text"); for (let i = 0; i < textInputs.length; ++i) { if (textInputs[i].getElementsByTagName("input")[0].type == "email") { const pattern = /.*[a-zA-Z0-9]+.*@.*\.[a-zA-Z]+/; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); } else if (textInputs[i].getElementsByTagName("input")[0].type == "text") { const patternStr = textInputs[i].getAttribute("data-regex"); const pattern = patternStr? new RegExp(patternStr): null; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", function(e) { checkText(pattern, e); }); } } function checkText(pattern, e) { if (pattern && e.target.value.search(pattern) == -1) { e.target.parentElement.classList.add("form__text--error"); } else { e.target.parentElement.classList.remove("form__text--error"); } }
* { margin: 0; padding: 0; box-sizing: border-box; }.form { margin: 10px; }.form.form__text { position: relative; margin: 2rem 0 4rem 0; display: block; }.form.form__text__label { position: absolute; font-size: 1.4rem; padding: 10px; opacity: 0.5; top: 50%; left: 0; pointer-events: none; transition: all 0.2s ease-out; transform: translateY(-50%); }.form.form__text__error-label { color: red; opacity: 0; transition: all 0.2s ease-out; position: absolute; top: 110%; left: 0; }.form.form__text input[type=text], .form.form__text input[type=email] { padding: 10px; width: 100%; border: 1px solid #ccc; border-radius: 5px; transition: all 0.2s ease-out; }.form.form__text input[type=text]:focus ~.form__text__label, .form.form__text input[type=text]:not(:placeholder-shown) ~.form__text__label, .form.form__text input[type=email]:focus ~.form__text__label, .form.form__text input[type=email]:not(:placeholder-shown) ~.form__text__label { transform: translateX(-15px) translateY(-125%) scale(0.75); opacity: 1; }.form.form__text input[type=text]:focus, .form.form__text input[type=email]:focus { outline: none; background: rgba(122, 217, 255, 0.075); }.form.form__text--error.form__text__label { color: red; }.form.form__text--error.form__text__error-label { opacity: 1; }.form.form__text--error input[type=text], .form.form__text--error input[type=email] { border: 1px solid red; }.form.form__text--error input[type=text]:focus, .form.form__text--error input[type=email]:focus { background: rgba(255, 0, 0, 0.05); }
<form class="form"> <label class="form__text"> <input type="email" id="email" name="email" placeholder=" " /> <span class="form__text__label">Email</span> <span class="form__text__error-label">Invalid Email</label> </label> <label class="form__text" data-regex="[a-zA-z ]{4,}"> <input type="text" id="name" name="name" placeholder=" " /> <span class="form__text__label">Name</span> <span class="form__text__error-label">Invalid Name</span> </label> <label class="form__text"> <input type="text" id="random" name="random" placeholder=" " /> <span class="form__text__label">Random Fact</span> </label> </form>
您可以使用 Babel 將代碼從 ES6+ 轉換為 ES5,因此您可以編寫上述代碼,但它會自動轉換為與新代碼相同的舊代碼。
注意: let
和const
將被 IE11 接受,但是它們的行為與var
完全相同。 IE11 僅使它們成為允許的語法,但作為var
的別名。 它們不會是塊范圍的。
如果你必須使用 ES5,那么這不是什么大問題。 問題是缺少塊作用域,只有全局作用域和函數作用域。 因此,您需要在功能 scope 中捕獲變量。 IIFE 可以做到這一點,但它看起來有點難看。
checkText()
我們可以改為使checkText
成為一個高階function ,它是柯里化的 - 它不是采用兩個參數,而是首先采用一個參數,然后返回一個采用第二個參數的 function。 這看起來像這樣:
function checkText(pattern){
return function(e) {
if (pattern && e.target.value.search(pattern) == -1) {
e.target.parentElement.classList.add("form__text--error");
} else {
e.target.parentElement.classList.remove("form__text--error");
}
}
}
它在這里是一個強大的構造,因為它允許我們在調用var returnedFunction = checkText(pattern)
時立即捕獲變量 - 現在即使變量模式在封閉上下文中發生變化, returnedFunction
仍將保留前一個。
作為獎勵,這使我們能夠消除一些無用的代碼。 讓我們一步一步來完全理解 - 首先這個
.addEventListener("input", function(e) {
checkText(pattern, e);
});
必須變成
.addEventListener("input", function(e) {
var returnedFunction = checkText(pattern);
returnedFunction(e);
});
因為我們現在必須將參數傳遞給兩個不同的函數。 此時仍然存在與之前相同的問題。 現在這並不能解決任何問題,但我想展示干預步驟。 同樣的問題 - 當偵聽器觸發時, checkText checkText(pattern)
將在該點pattern
已更改時執行。
checkText(pattern)
捕獲正確的值我們需要確保它在事件偵聽器中初始化之前觸發:
var pattern = patternStr ? new RegExp(patternStr) : null;
var returnedFunction = checkText(pattern);
/* ...code... */
.addEventListener("input", function(e) {
returnedFunction(e);
});
當我們將它放在與pattern
變量相同的級別時,就在.addEventListener
回調之外,它可以按預期工作而無需使用 IIFE。 我們將捕獲當前模式變量,對其進行更改不會影響returnedFunction
。
然而,回調 function 現在是 this function(e) { returnedFunction(e); }
- 一個function(e) { returnedFunction(e); }
接受一個參數並調用一個 function 傳遞相同的參數作為參數。 調用<anonymous function>(e)
與調用returneFunction(e)
相同。 外部包裝器 function 和內部returneFunction
具有完全相同的簽名,並且使用單個參數調用它們的語義幾乎相同。 因此,包裝器現在沒用了。 我們可以刪除它並執行 lambda 演算調用 Eta 歸約來簡化抽象,因此整個回調變成
var pattern = patternStr ? new RegExp(patternStr) : null;
var returnedFunction = checkText(pattern);
/* ...code... */
.addEventListener("input", returnedFunction);
現在事件監聽器只是returnedFunction
函數。 當它被觸發時,它將傳遞與以前相同的參數。
最后,簡化的最后一步是內聯checkText(pattern)
調用。 我們在這里並不需要額外的變量。 擺脫它並擁有它很容易
.addEventListener("input", checkText(pattern));
完畢。 這很詳細,因為我想展示這個過程。 實際上,它只是將checkText()
function 轉換為咖喱變體,然后用它替換回調。 希望這些步驟將它從一些奇怪的指令轉變為理解為什么要這樣做。
最終結果是這樣的:
var textInputs = document.getElementsByClassName("form__text"); for (var i = 0; i < textInputs.length; ++i) { if (textInputs[i].getElementsByTagName("input")[0].type == "email") { var pattern = /.*[a-zA-Z0-9]+.*@.*\.[a-zA-Z]+/; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", checkText(pattern)); } else if (textInputs[i].getElementsByTagName("input")[0].type == "text") { var patternStr = textInputs[i].getAttribute("data-regex"); var pattern = patternStr? new RegExp(patternStr): null; textInputs[i].getElementsByTagName("input")[0].addEventListener("input", checkText(pattern)); } } function checkText(pattern){ return function(e) { if (pattern && e.target.value.search(pattern) == -1) { e.target.parentElement.classList.add("form__text--error"); } else { e.target.parentElement.classList.remove("form__text--error"); } } }
* { margin: 0; padding: 0; box-sizing: border-box; }.form { margin: 10px; }.form.form__text { position: relative; margin: 2rem 0 4rem 0; display: block; }.form.form__text__label { position: absolute; font-size: 1.4rem; padding: 10px; opacity: 0.5; top: 50%; left: 0; pointer-events: none; transition: all 0.2s ease-out; transform: translateY(-50%); }.form.form__text__error-label { color: red; opacity: 0; transition: all 0.2s ease-out; position: absolute; top: 110%; left: 0; }.form.form__text input[type=text], .form.form__text input[type=email] { padding: 10px; width: 100%; border: 1px solid #ccc; border-radius: 5px; transition: all 0.2s ease-out; }.form.form__text input[type=text]:focus ~.form__text__label, .form.form__text input[type=text]:not(:placeholder-shown) ~.form__text__label, .form.form__text input[type=email]:focus ~.form__text__label, .form.form__text input[type=email]:not(:placeholder-shown) ~.form__text__label { transform: translateX(-15px) translateY(-125%) scale(0.75); opacity: 1; }.form.form__text input[type=text]:focus, .form.form__text input[type=email]:focus { outline: none; background: rgba(122, 217, 255, 0.075); }.form.form__text--error.form__text__label { color: red; }.form.form__text--error.form__text__error-label { opacity: 1; }.form.form__text--error input[type=text], .form.form__text--error input[type=email] { border: 1px solid red; }.form.form__text--error input[type=text]:focus, .form.form__text--error input[type=email]:focus { background: rgba(255, 0, 0, 0.05); }
<form class="form"> <label class="form__text"> <input type="email" id="email" name="email" placeholder=" " /> <span class="form__text__label">Email</span> <span class="form__text__error-label">Invalid Email</label> </label> <label class="form__text" data-regex="[a-zA-z ]{4,}"> <input type="text" id="name" name="name" placeholder=" " /> <span class="form__text__label">Name</span> <span class="form__text__error-label">Invalid Name</span> </label> <label class="form__text"> <input type="text" id="random" name="random" placeholder=" " /> <span class="form__text__label">Random Fact</span> </label> </form>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.