每当有人 Select 休息日(如星期六或星期日,它可能是一系列 checkout 日中的任何一天)时,我都想呈现一个错误.

我有超文本标记语言:

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

<br>

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

Css:以下内容:

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

以下是该脚本:

'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所指出的,以编程方式设置输入的值does not trigger a 100 event.所以一定有什么东西导致了事件的两次触发.

我非常抱歉没有在 firefox 以外的其他浏览器上测试.这个问题似乎只出现在Firefox中,就像在基于 chromium 的浏览器中一样,一切都像预期的那样工作(尽管在Firefox Android中也可以).所以,我猜这是Firefox中的某种错误,我一个人无法确认.请try 在不同的浏览器中运行代码片段.

'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>

推荐答案

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

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的try 离成功不远了.事实证明,下面的方法可以不监听第二个事件(尽管下面有更好的解决方案)

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

似乎Firefox不会立即调度额外的事件(所以不是在同一个调用堆栈中).因此,在重新连接监听程序之前添加超时会有所帮助.不过,如果像@Isherwood建议的那样使用布尔标志,代码可能会更加透明和高效.

正如@AayushKarna在 comments 中指出的那样,在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中,更改的值也只打印一次,并且输入字段也会明显重置.

请随意添加更多信息.这是一个社区的答案.

Javascript相关问答推荐

如何解决这个未能在响应上执行json:body stream已读问题?

确定MutationRecord中removedNodes的索引

按钮未放置在html dis位置

useNavigation更改URL,但不呈现或显示组件

在Angular中将样式应用于innerHTML

类型自定义lazy Promise. all

分层树视图

无法访问Vue 3深度监视器中对象数组的特定对象值'

在open shadow—root中匹配时,使用jQuery删除一个封闭的div类

为什么我的导航条打开状态没有在窗口addeventlistener(Reaction Js)中更新?

Node.js错误: node 不可单击或不是元素

Socket.IO在刷新页面时执行函数两次

JavaScript将字符串数字转换为整数

如何在独立的Angular 应用程序中添加Lucide-Angel?

设置复选框根据选中状态输入选中值

对不同目录中的Angular material 表列进行排序

$GTE的mongoose 问题

将字体样式应用于material -UI条形图图例

我想为我的Reaction项目在画布上加载图像/视频,图像正在工作,但视频没有

如何从图表中映射一组图表-js使用REACT