以编程方式在更改事件处理程序中设置日期输入的值会触发 Firefox 中的更改事件

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

每当有人选择休息日的日期(例如星期六或星期日,它可能是一系列关闭日中的任何一天)时,我想呈现错误。

我有 HTML:

<div>
  <input type="date" id="date-input">
</div>

<br>

<div id="error-container">
</div>

CSS:

.d-error{
  background: orangered;
  color: white;
  padding: 4px 8px;
}

这是 JavaScript:

'use strict';

const dateInput = document.getElementById('date-input');
const errorContainer = document.getElementById('error-container');
const closingDays = [0, 6];
const weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday','Friday', 'Saturday'];

const displayError = function ({ parentEl, message, position = 'prepend', durationSec = 0 }) {
  const errorElement = document.createElement('p');
  errorElement.classList.add('d-error');
  errorElement.textContent = message;
  parentEl[position](errorElement);

  if (durationSec) {
    setTimeout(() => {
      errorElement.remove();
    }, durationSec * 1000);
  }
};

const handleDateChange = function (event) {
  const selectedDay = new Date(event.currentTarget.value).getDay();

  if (closingDays.includes(selectedDay)) {
    displayError({
      parentEl: errorContainer,
      message: `We are closed on ${weekDays[selectedDay]}. Please select another day.`,
      durationSec: 5
    });

    event.currentTarget.value = '';
  }
};

dateInput.addEventListener('change', handleDateChange);

这可行,但错误会呈现两次。一次是由于用户更改,一次是由于以编程方式更改输入值

event.currentTarget.value = '';
。那么,如何暂时删除
change
事件监听器并重新附加到它呢?或者有其他方法可以解决这个问题吗?

我尝试过:

const handleDateChange = function(event) {
  ...

  event.currentTarget.removeEventListener('change', handleDateChange);
  event.currentTarget.value = '';  
  event.currentTarget.addEventListener('change', handleDateChange);

  ...
}

但这并没有解决问题。

更新:

首先,正如@Sebastian 所指出的,以编程方式设置输入的值不会触发

change
事件。所以一定还有其他原因导致事件触发两次。

我非常抱歉没有在 Firefox 以外的其他浏览器中进行测试。这个问题似乎只出现在 Firefox 中,因为在基于 chromium 的浏览器中,一切都按预期工作(尽管在 Firefox android 中工作)。所以,我猜测这是 Firefox 中的某种错误,但我个人无法确认。请尝试在不同的浏览器中运行代码片段。

'use strict';

const dateInput = document.getElementById('date-input');
const errorContainer = document.getElementById('error-container');
const closingDays = [0, 6];
const weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

const displayError = function({
  parentEl,
  message,
  position = 'prepend',
  durationSec = 0
}) {
  const errorElement = document.createElement('p');
  errorElement.classList.add('d-error');
  errorElement.textContent = message;
  parentEl[position](errorElement);

  if (durationSec) {
    setTimeout(() => {
      errorElement.remove();
    }, durationSec * 1000);
  }
};

const handleDateChange = function(event) {
  const selectedDay = new Date(event.currentTarget.value).getDay();

  if (closingDays.includes(selectedDay)) {
    displayError({
      parentEl: errorContainer,
      message: `We are closed on ${weekDays[selectedDay]}. Please select another day.`,
      durationSec: 5
    });

    event.currentTarget.value = '';
  }
};

dateInput.addEventListener('change', handleDateChange);
.d-error {
  background: orangered;
  color: white;
  padding: 4px 8px;
}
<div>
  <input type="date" id="date-input">
</div>

<br>

<div id="error-container">
</div>

javascript dom dom-events
1个回答
2
投票

这里是一个较短版本的代码片段,用于在 Firefox (121) 中重现该错误。 OP 是正确的,第二个事件实际上是由以编程方式设置事件处理程序内的输入字段的值引起的(根据第一个注释在规范中,情况不应该是这样)。但是,第二个事件中打印到控制台的值与第一个事件的值相同。此外,在事件处理程序之外设置值根本不会触发事件(应该如此)。与此同时,OP here on bugzilla 报告了这种不一致的行为(它可能与 this other 有关)。

function onChange(event) {
  console.log('changed to ' + dateInput.value)
  dateInput.value = ''; // somehow triggers a second event in Firefox
};

dateInput.addEventListener('change', onChange);
dateInput.value = '2024-02-07'; // does not trigger the event!
<div>
  <input type="date" id="dateInput">
</div>

其他浏览器似乎没问题。特别是 Chromium 121 的行为符合预期(只有一个事件)。 作为 Firefox 的解决方法,OP 的尝试离成功并不遥远。事实证明,以下方法适用于不监听第二个事件(尽管请参阅下面更好的解决方案)

dateInput.removeEventListener('change', onChange);
dateInput.value = ''; // somehow triggers a second event
setTimeout(() => dateInput.addEventListener('change', onChange), 0);

Firefox 似乎没有立即调度额外的事件(因此不在同一调用堆栈内)。因此,在重新连接侦听器之前添加超时会有所帮助。如果您使用布尔标志,代码可能会更加透明和高性能,正如 @isherwood 所建议的。

正如 @AayushKarna 的评论中所指出的,在 Firefox 中仍然缺少在事件处理程序中重置输入字段的可见反馈。因此,还必须设置额外的超时时间。但随后它就不再触发 Firefox 中的事件,因此删除事件侦听器就变得多余了。我仍然在答案中保留上述建议,因为问题中明确要求了这一点。尽管如此,现在更简单的解决方法片段是:

function onChange(event) {
  console.log('changed to ' + dateInput.value)
  setTimeout(() => dateInput.value = '', 0);
};

dateInput.addEventListener('change', onChange);
<div>
  <input type="date" id="dateInput">
</div>

即使在 Firefox 中,更改的值也仅打印一次,并且输入字段也明显重置。

随时添加更多信息。这是社区的答案...

© www.soinside.com 2019 - 2024. All rights reserved.