你能模糊一组复选框吗?

Can you onblur a set of checkboxes?

我有一组复选框和验证,以便在提交时至少需要选中其中一个复选框。但我希望能够在用户模糊复选框集时设置验证错误。问题是,如果我对最后一个进行模糊处理,则它不起作用,因为它们可能会通过 shift-tabbing 切换到最后一个复选框的第二个。我试过但是 fieldset 标签中的 onblur ,但这根​​本没有触发模糊。这只是普通 html 和香草 JS 无法克服的限制吗?

您可以查看下一个获得焦点的元素是什么,并确定它们是否属于同一组。

document.querySelectorAll('input[type="checkbox"]').forEach((elem) => {
  elem.addEventListener("blur", function (event) {
    const nextElem = event.relatedTarget;
    const fieldSet = elem.closest('fieldset');
    const isValid = fieldSet.querySelector("input:checked") !== null;
    if (nextElem?.closest('fieldset') !== fieldSet) {
        fieldSet.classList.toggle("error", !isValid);
    } else if (isValid) {
        fieldSet.classList.remove("error");
    }
  });
});
.error {
  color: red;
}
<form>
  <fieldset>
    <legend>Pizza One</legend>

    <input type="checkbox" name="x1-1" id="x1-1">
    <label for="x1-1">Cheese</label>

    <input type="checkbox" name="x1-2" id="x1-2">
    <label for="x1-2">Peppers</label>

    <input type="checkbox" name="x1-3" id="x1-3">
    <label for="x1=3">Mushrooms</label>

  </fieldset>

  <fieldset>
    <legend>Pizza Two</legend>

    <input type="checkbox" name="x2-1" id="x2-1">
    <label for="x2-1">Cheese</label>

    <input type="checkbox" name="x2-2" id="x2-2">
    <label for="x2-2">Peppers</label>

    <input type="checkbox" name="x2-3" id="x2-3">
    <label for="x2-3">Mushrooms</label>

  </fieldset>

  <input type="submit" />
</form>

添加技巧以使用 HTML5 验证

document.querySelectorAll('input[type="checkbox"]').forEach((elem) => {
  elem.addEventListener("blur", function(event) {
    const nextElem = event.relatedTarget;
    const fieldSet = elem.closest('fieldset');
    const isValid = fieldSet.querySelector("input:checked") !== null;
    if (nextElem?.closest('fieldset') !== fieldSet) {
      fieldSet.classList.toggle("error", !isValid);
    }
  });

  elem.addEventListener("change", function(event) {
    const fieldSet = elem.closest('fieldset');
    const isValid = fieldSet.querySelector("input:checked") !== null;
    if (isValid) {
      fieldSet.classList.remove("error");
      fieldSet.querySelectorAll("input").forEach((cb) => {
        cb.removeAttribute("required");
      });
    } else {
      fieldSet.querySelectorAll("input").forEach((cb) => {
        cb.setAttribute("required", "required");
      });
    }
  });

  const changeEvt = document.createEvent("HTMLEvents");
  changeEvt.initEvent("change", false, true);
  elem.dispatchEvent(changeEvt);
});
.error {
  color: red;
}
<form>
  <fieldset>
    <legend>Pizza One</legend>

    <input type="checkbox" name="x1-1" id="x1-1">
    <label for="x1-1">Cheese</label>

    <input type="checkbox" name="x1-2" id="x1-2">
    <label for="x1-2">Peppers</label>

    <input type="checkbox" name="x1-3" id="x1-3">
    <label for="x1=3">Mushrooms</label>

  </fieldset>

  <fieldset>
    <legend>Pizza Two</legend>

    <input type="checkbox" name="x2-1" id="x2-1">
    <label for="x2-1">Cheese</label>

    <input type="checkbox" name="x2-2" id="x2-2">
    <label for="x2-2">Peppers</label>

    <input type="checkbox" name="x2-3" id="x2-3">
    <label for="x2-3">Mushrooms</label>

  </fieldset>

  <input type="submit" />
</form>

根据 epascarello 的建议,我能够创建一个函数来提供与所提供的函数类似的功能。我在复选框下方有一个错误范围。如果选中任何复选框,我想要的行为会立即删除它,但只有在用户模糊整个复选框集时才添加错误范围。请注意,此页面上只有一个字段集,错误范围有一个我可以参考的特定 ID。这是我开始工作的功能:

document.querySelectorAll('input[type="checkbox"]').forEach((elem) => {
            elem.addEventListener("blur", function (event) {
                var errorSpan = document.getElementById("checkboxesErrorSpan");
                var isValid = document.querySelector("fieldset").querySelectorAll("input:checked").length > 0;
                if (isValid) {
                    if ((errorSpan.style.display == "inline")) {
                        errorSpan.style.display = "none";
                    }
                }
                else {
                    if (event.relatedTarget.type != "checkbox") {
                        if ((errorSpan.style.display == "none")) {
                            errorSpan.style.display = "inline";
                        }
                    }
                }
                
            });
        });