将事件监听器应用于多个dom元素

4
我创建了一个 JavaScript 代码来自动格式化一些日期输入。问题是我的代码只在第一个输入上运作,而不是其他的。如何让我的代码应用在表单中的所有日期输入上?
有没有办法使事件监听器在所有输入上被激活?
我想过为每个输入添加不同的 ID 并复制代码,但这会使我的代码难以阅读。
有什么提示吗?我尝试使用 for 循环遍历输入,但它也无效。

var dates = document.getElementsByTagName('input');
for (i = 0; i < 3; i++) {
date = document.getElementsByTagName('input')[i]
function checkValue(str, max) {
  if (str.charAt(0) !== '0' || str == '00') {
    var num = parseInt(str);
    if (isNaN(num) || num <= 0 || num > max) num = 1;
    str = num > parseInt(max.toString().charAt(0)) && num.toString().length == 1 ? '0' + num : num.toString();
  };
  return str;
};

date.addEventListener('input', function(e) {
  this.type = 'text';
  var input = this.value;
  if (/\D\/$/.test(input)) input = input.substr(0, input.length - 3);
  var values = input.split('/').map(function(v) {
    return v.replace(/\D/g, '')
  });
  if (values[0]) values[0] = checkValue(values[0], 12);
  if (values[1]) values[1] = checkValue(values[1], 31);
  var output = values.map(function(v, i) {
    return v.length == 2 && i < 2 ? v + ' / ' : v;
  });
  this.value = output.join('').substr(0, 14);
});

date.addEventListener('blur', function(e) {
  this.type = 'text';
  var input = this.value;
  var values = input.split('/').map(function(v, i) {
    return v.replace(/\D/g, '')
  });
  var output = '';
  
  if (values.length == 3) {
    var year = values[2].length !== 4 ? parseInt(values[2]) + 2000 : parseInt(values[2]);
    var month = parseInt(values[0]) - 1;
    var day = parseInt(values[1]);
    var d = new Date(year, month, day);
    if (!isNaN(d)) {
      document.getElementById('result').innerText = d.toString();
      var dates = [d.getMonth() + 1, d.getDate(), d.getFullYear()];
      output = dates.map(function(v) {
        v = v.toString();
        return v.length == 1 ? '0' + v : v;
      }).join(' / ');
    };
  };
  this.value = output;
});
}
html {
  box-sizing: border-box;
  font-family: 'PT Sans', sans-serif;
-webkit-font-smoothing: antialiased;
}
*, 
*:before, 
*:after {
  box-sizing: inherit;
}
body {
    background-color: #f3f3f3;
}
form {
    width: 100%;
    max-width: 400px;
    margin: 60px auto;
}
form input {
    font-size: 30px;
    padding: 0 20px;
    border: 2px solid #ccc;
    width: 100%;
    color: #666;
    line-height: 3;
    border-radius: 7px;
    font-family: 'PT Sans', sans-serif;
    font-weight: bold;
}
form input:focus {
    outline: 0;
}
form input.error {
    border-color: #ff0000;  
}
form label.error {
    background-color: #ff0000;
    color: #fff;
    padding: 6px;
    font-size: 11px;
}

label {
    color: #999;
    display: block;
    margin-bottom: 10px;
    text-transform: uppercase;
    font-size: 18px;
    font-weight: bold;
    letter-spacing: 0.05em
}
form small {
    color: #888;
    font-size: 1em;
    margin-top: 10px;
    display: block;
    align-self: ;
}
<form id="form" method="post" action="">
  <label for="amount">Date</label>
  <input type="text" id="date1" />
  <small>Enter date as Month / Day / Year</small>
  </br>
  <label for="amount">Date 2</label>
  <input type="text" id="date2" />
  <small>Enter date as Month / Day / Year</small>
  </br>
  <label for="amount">Date 3</label>
  <input type="text" id="date3" />
  <small>Enter date as Month / Day / Year</small>
  
</form>

编辑:我更正了输入ID,使其成为唯一的。

编辑2:在我的解决方案中,我正在解析输入并在每个斜杠之前和之后添加空格。我想要消除这些空格。为此,我修改了这一行:

this.value = output.join('').substr(0, 14);

我将14替换为10。但是这个修改导致了数据删除的不可能。 例如:06/07/无法被删除。


所有的DOM元素必须具有唯一的ID。 - brk
1
这不是问题。但是没错,答案也涵盖了这一评论。 - Alouani Younes
请在提问之前检查代码片段是否有效,除了为什么不起作用以外的其他问题。对于“有没有办法...”部分的答案是“是的,它被称为event delegation”。 - traktor
1
谢谢您的评论。我已经更正了代码(使用var date代替dates)。 - Alouani Younes
2个回答

2
querySelectorAll比getElementsBy*方法更易于使用,它返回一个静态的NodeList而不是HTMLCollection。与HTMLCollection不同,NodeList可以直接迭代,迭代时不会发生变化,并且更加灵活。
可以使用querySelectorAll('input').forEach直接迭代每个输入项。此外,请注意您的HTML无效:文档中不应该有多个具有相同ID的元素。由于您省略了一些HTML,因此下面的代码片段在最后会导致错误。

document.querySelectorAll('input').forEach((date) => {

  function checkValue(str, max) {
    if (str.charAt(0) !== '0' || str == '00') {
      var num = parseInt(str);
      if (isNaN(num) || num <= 0 || num > max) num = 1;
      str = num > parseInt(max.toString().charAt(0)) && num.toString().length == 1 ? '0' + num : num.toString();
    };
    return str;
  };

  date.addEventListener('input', function(e) {
    this.type = 'text';
    var input = this.value;
    if (/\D\/$/.test(input)) input = input.substr(0, input.length - 3);
    var values = input.split('/').map(function(v) {
      return v.replace(/\D/g, '')
    });
    if (values[0]) values[0] = checkValue(values[0], 12);
    if (values[1]) values[1] = checkValue(values[1], 31);
    var output = values.map(function(v, i) {
      return v.length == 2 && i < 2 ? v + ' / ' : v;
    });
    this.value = output.join('').substr(0, 14);
  });

  date.addEventListener('blur', function(e) {
    this.type = 'text';
    var input = this.value;
    var values = input.split('/').map(function(v, i) {
      return v.replace(/\D/g, '')
    });
    var output = '';

    if (values.length == 3) {
      var year = values[2].length !== 4 ? parseInt(values[2]) + 2000 : parseInt(values[2]);
      var month = parseInt(values[0]) - 1;
      var day = parseInt(values[1]);
      var d = new Date(year, month, day);
      if (!isNaN(d)) {
        document.getElementById('result').innerText = d.toString();
        var dates = [d.getMonth() + 1, d.getDate(), d.getFullYear()];
        output = dates.map(function(v) {
          v = v.toString();
          return v.length == 1 ? '0' + v : v;
        }).join(' / ');
      };
    };
    this.value = output;
  });
});
html {
  box-sizing: border-box;
  font-family: 'PT Sans', sans-serif;
  -webkit-font-smoothing: antialiased;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  background-color: #f3f3f3;
}

form {
  width: 100%;
  max-width: 400px;
  margin: 60px auto;
}

form input {
  font-size: 30px;
  padding: 0 20px;
  border: 2px solid #ccc;
  width: 100%;
  color: #666;
  line-height: 3;
  border-radius: 7px;
  font-family: 'PT Sans', sans-serif;
  font-weight: bold;
}

form input:focus {
  outline: 0;
}

form input.error {
  border-color: #ff0000;
}

form label.error {
  background-color: #ff0000;
  color: #fff;
  padding: 6px;
  font-size: 11px;
}

label {
  color: #999;
  display: block;
  margin-bottom: 10px;
  text-transform: uppercase;
  font-size: 18px;
  font-weight: bold;
  letter-spacing: 0.05em
}

form small {
  color: #888;
  font-size: 1em;
  margin-top: 10px;
  display: block;
  align-self: ;
}
<form id="form" method="post" action="">
  <label for="amount">Date</label>
  <input type="text" id="date" />
  <small>Enter date as Month / Day / Year</small>
  </br>
  <label for="amount">Date 2</label>
  <input type="text" id="date" />
  <small>Enter date as Month / Day / Year</small>
  </br>
  <label for="amount">Date 3</label>
  <input type="text" id="date" />
  <small>Enter date as Month / Day / Year</small>

</form>


非常快速的回答。谢谢! - Alouani Younes
我应该修改什么以避免在斜杠前后添加空格? 我更改了这一行: this.value = output.join('').substr(0, 14); 为: this.value = output.join('').substr(0, 10); 但是,我无法删除日期进行修改。 - Alouani Younes

1
你可以通过监听共同父节点上的事件并仅处理特定目标触发的事件来以相同方式处理多个节点上的事件。该方法称为“事件委托”,例如this question中讨论,还可轻松找到其他相关的讨论和问题。
要在此情况下应用该方法,我将日期处理程序函数声明为命名函数(它们也可以内联编写),并向每个日期输入添加了一个 data-type="date" 属性值以识别它们(也可以找到其他方法),并在事件到达表单元素时检查事件。

function checkValue(str, max) {
  if (str.charAt(0) !== '0' || str == '00') {
    var num = parseInt(str);
    if (isNaN(num) || num <= 0 || num > max) num = 1;
    str = num > parseInt(max.toString().charAt(0)) && num.toString().length == 1 ? '0' + num : num.toString();
  };
  return str;
};
  
function dateInput(date) {  // date is input object argument
  var input = date.value;
  if (/\D\/$/.test(input))
    input = input.substr(0, input.length - 3);
  var values = input.split('/').map(function(v) {
    return v.replace(/\D/g, '')
  });
  if (values[0]) {
    values[0] = checkValue(values[0], 12);
  }
  if (values[1]) {
    values[1] = checkValue(values[1], 31);
  }
  var output = values.map(function(v, i) {
    return v.length == 2 && i < 2 ? v + ' / ' : v;
  });
  date.value = output.join('').substr(0, 14);
}

function dateBlur(date) {  // date is input object argument
  var input = date.value;
  var values = input.split('/').map(function(v, i) {
    return v.replace(/\D/g, '')
  });
  var output = '';
  if (values.length == 3) {
    var year = values[2].length !== 4 ? parseInt(values[2]) + 2000 : parseInt(values[2]);
    var month = parseInt(values[0]) - 1;
    var day = parseInt(values[1]);
    var d = new Date(year, month, day);
    if (!isNaN(d)) {
      document.getElementById('result').innerText = d.toString();
      var dates = [d.getMonth() + 1, d.getDate(), d.getFullYear()];
      output = dates.map(function(v) {
        v = v.toString();
        return v.length == 1 ? '0' + v : v;
      }).join(' / ');
    }
  }
  date.value = output;
}
// delegate event handling from the form element

var form = document.getElementById("form");
form.addEventListener("blur", function(e) {
  if(e.target.dataset.type == "date") {
    dateBlur(e.target);
  }
},false);
form.addEventListener("input", function(e) {
  if(e.target.dataset.type == "date") {
    dateInput(e.target);
  }
}, false);
html {
  box-sizing: border-box;
  font-family: 'PT Sans', sans-serif;
-webkit-font-smoothing: antialiased;
}
*, 
*:before, 
*:after {
  box-sizing: inherit;
}
body {
    background-color: #f3f3f3;
}
form {
    width: 100%;
    max-width: 400px;
    margin: 60px auto;
}
form input {
    font-size: 30px;
    padding: 0 20px;
    border: 2px solid #ccc;
    width: 100%;
    color: #666;
    line-height: 3;
    border-radius: 7px;
    font-family: 'PT Sans', sans-serif;
    font-weight: bold;
}
form input:focus {
    outline: 0;
}
form input.error {
    border-color: #ff0000;  
}
form label.error {
    background-color: #ff0000;
    color: #fff;
    padding: 6px;
    font-size: 11px;
}

label {
    color: #999;
    display: block;
    margin-bottom: 10px;
    text-transform: uppercase;
    font-size: 18px;
    font-weight: bold;
    letter-spacing: 0.05em
}
form small {
    color: #888;
    font-size: 1em;
    margin-top: 10px;
    display: block;
    align-self: ;
}
<form id="form" method="post" action="">
  <label for="amount">Date</label>
  <input type="text" id="date1" data-type="date" />
  <small>Enter date as Month / Day / Year</small>
  </br>
  <label for="amount">Date 2</label>
  <input type="text" id="date2" data-type="date" />
  <small>Enter date as Month / Day / Year</small>
  </br>
  <label for="amount">Date 3</label>
  <input type="text" id="date3" data-type="date" />
  <small>Enter date as Month / Day / Year</small>
  
</form>


它运行得非常好。感谢您的解决方案。它允许轻松地定位数据字段。 - Alouani Younes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接