还有比使用IIFE更好的方法吗?

问题描述 投票:0回答:1

我正在尝试为表单创建模板,该模板检查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变量。我很好奇是否有更清洁的方法可以做到这一点。我假设创建正则表达式对象会花费一些钱,这就是为什么我试图在回调之外创建它们的原因。预先感谢。

javascript iife
1个回答
1
投票

问题是pattern将是整个函数的共享变量名。然后,当事件监听器触发时,它将获取pattern的最新版本,而不是当前版本。是a classic problem

ES6解决方案

通过引入letlet,您可以在块范围内使用变量,因此解决方案非常简单-将任何const更改为constvar

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");
  }
}

[您可以使用Babel将您的代码从ES6 +转换为ES5,因此您应该编写上面的代码,但是它将自动转换为与新代码相同的旧代码。

NOTE: * { 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>将被IE11 但是接受,它们的行为与let完全相同。 IE11仅使它们成为允许的语法,但作为const的别名。它们不会被块作用域限制。

ES5解决方案

如果您必须使用ES5,那么这不是什么大问题。问题是缺少块作用域,只有全局作用域和功能作用域。因此,您需要在功能范围内捕获变量。 IIFE可以这样做,但是看起来有点难看。

咖喱var

我们可以使var成为checkText()的高阶函数-代替采用两个参数,它首先采用一个参数,然后返回采用第二个参数的函数。看起来像这样:

checkText

这里是一个强大的结构,因为如果调用curried,它使我们可以立即捕获变量-现在即使变量模式在封闭上下文中发生变化,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

因为我们现在必须将参数传递给两个不同的函数。 目前仍然存在与以前相同的问题。现在这并不能解决任何问题,但是我想展示一下中间的步骤。同样的问题-当监听器触发时,then .addEventListener("input", function(e) { checkText(pattern, e); }); 将在已更改.addEventListener("input", function(e) { var returnedFunction = checkText(pattern); returnedFunction(e); }); 的位置执行。

确保checkText(pattern)捕获正确的值

我们需要确保它在事件监听器中初始化的[[之前中触发:

pattern
[当我们将其放置在与checkText(pattern)变量相同的级别上时,因此在var pattern = patternStr ? new RegExp(patternStr) : null;
var returnedFunction = checkText(pattern);
/* ...code... */
.addEventListener("input", function(e) {
  returnedFunction(e);
});
回调之外,它可以按预期工作,而无需使用IIFE。我们将捕获

current

模式变量,对其所做的更改将不会影响pattern简化抽象

但是,现在的回调函数是此.addEventListener-一个接受参数并调用将相同参数作为参数传递的函数。调用returnedFunction与调用function(e) { returnedFunction(e); }

相同

。外部包装函数和内部<anonymous function>(e)具有完全相同的签名,并且使用单个参数调用它们的语义实际上是相同的。因此,包装器现在已无用。我们可以删除它并执行lambda演算所称的Eta简化来简化抽象,因此整个回调变为[]returneFunction(e)
现在事件监听器只是returneFunction。触发后,将传递与以前相同的参数。

删除多余的变量

最后,简化的最后一步是仅内嵌var pattern = patternStr ? new RegExp(patternStr) : null; var returnedFunction = checkText(pattern); /* ...code... */ .addEventListener("input", returnedFunction); 调用。我们实际上不需要这里的额外变量。摆脱它并拥有

很容易

returnedFunction

完成。之所以详细,是因为我想显示该过程。实际上,它只是将checkText(pattern)函数转换为咖喱变量,然后将其替换为回调函数。希望这些步骤可以将其从发生的一些怪异说明转变为理解为什么这样做。

最终结果是这样:

.addEventListener("input", checkText(pattern));
checkText()
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");
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.