按住 keydown 问题时单击时的 Firefox 事件传播

Firefox Event propagation on click while holding keydown problem

我正在实施 table 行多 selection,它应该像这样工作: 单击第一行,然后按住 Shift 键并单击底行应该 select 之间的所有行。 我还使用自定义复选框,基本上是复选框 + 标签的组合。我也仅在 table 上利用事件传播和绑定事件处理程序。

问题是在 Firefox 上按住 Shift 键时事件不会从标签冒泡到输入。

var lastCheckedRow = null;

$("table").click(function handleRowCheckboxClick(e) {
  var $t = $(e.target);
  // check if target is checkbox, otherwise ignore event ( on label )
  if ($t.is(":checkbox")) {
    var $cb = $t.is(":checkbox") ? $t : $t.siblings("input.mof-cell-checkbox");
    // if not disabled
    if (!$cb.prop("disabled")) {
      if ($cb.hasClass("mof-cell-checkbox")) {
        if (window.getSelection().empty) {
          // Chrome
          window.getSelection().empty();
        }
        checkBoxClickHandler($cb, e.shiftKey);
      }
    }
  }
});

function checkBoxClickHandler($checkBoxInput, wasShiftPressed) {
  var isChecked = $checkBoxInput.prop("checked");
  $checkBoxInput
    .closest("table > tbody > tr")
    .toggleClass("row-selected", isChecked);

  if (
    lastCheckedRow &&
    lastCheckedRow.index() ==
      $checkBoxInput.closest("table > tbody > tr").index()
  ) {
    lastCheckedRow = null;
    return;
  }

  if (!lastCheckedRow) {
    lastCheckedRow = $checkBoxInput.closest("table > tbody > tr");
    return;
  }

  if (wasShiftPressed) {
    var start = $checkBoxInput.closest("table > tbody > tr").index();
    var end = lastCheckedRow.index();
    var $trs = $checkBoxInput
      .closest("table")
      .find("tbody > tr")
      .slice(Math.min(start, end), Math.max(start, end) + 1);
    if (
      lastCheckedRow.find(".mof-cell-checkbox").prop("checked") == isChecked
    ) {
      $trs.each(function () {
        $(this).toggleClass("row-selected", isChecked);
        $(this).find(".mof-cell-checkbox").prop("checked", isChecked);
      });
    } else {
      $trs.each(function () {
        $(this).toggleClass("row-selected", !isChecked);
        $(this).find(".mof-cell-checkbox").prop("checked", !isChecked);
      });
    }
  }

  lastCheckedRow = $checkBoxInput.closest("table > tbody > tr");
}
.custom-checkbox {
  padding-left: 0;
  margin-top: -0.075rem;
  margin-bottom: -0.075rem;
  position: relative;
  display: block;
  min-height: 1.45rem;
}

.custom-control-input {
  position: absolute;
  z-index: -1;
  opacity: 0;
}

.custom-checkbox .custom-control-label::before {
  border-radius: 2px;
}

.custom-control-label::before {
  position: absolute;
  top: 0.1rem;
  left: 0;
  display: block;
  width: 1.25rem;
  height: 1.25rem;
  pointer-events: none;
  content: "";
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
  background-color: #dee2e6;
}

.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {
  background-color: #2196f3;
}

.custom-control-input:checked ~ .custom-control-label::before {
  border: none;
}
.custom-control-input:checked ~ .custom-control-label::before {
  color: #fff;
  background-color: #2196f3;
}

.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E");
}
.custom-control-label::after {
  position: absolute;
  top: 0.1rem;
  left: 0;
  display: block;
  width: 1.25rem;
  height: 1.25rem;
  content: "";
  background-repeat: no-repeat;
  background-position: center center;
  background-size: 50% 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
  <colgroup>
    <col width="30px">
  </colgroup>
  <tbody>
    <tr>
      <td>
        <div class="custom-checkbox custom-control">
          <input class="custom-control-input mof-cell-checkbox mof-cell-checkbox-item form-control" id="0__checkBox" type="checkbox" value="true">
          <label class="custom-control-label" for="0__checkBox"></label>
        </div>
      </td>
      <td>1
      </td>
    </tr>
    <tr>
      <td>
        <div class="custom-checkbox custom-control">
          <input class="custom-control-input mof-cell-checkbox mof-cell-checkbox-item form-control" id="1__checkBox" type="checkbox" value="true">
          <label class="custom-control-label" for="1__checkBox"></label>
        </div>
      </td>
      <td>2
      </td>
    </tr>
    <tr>
      <td>
        <div class="custom-checkbox custom-control">
          <input class="custom-control-input mof-cell-checkbox mof-cell-checkbox-item form-control" id="2__checkBox" type="checkbox" value="true">
          <label class="custom-control-label" for="2__checkBox"></label>
        </div>
      </td>
      <td>3
      </td>
    </tr>
  </tbody>
</table>

该演示适用于 Chrome,但不适用于 Firefox。 为什么会发生任何想法?谢谢!

shift+click 或 ctrl+click 标签在 Firefox 中不起作用。请参考他们重新打开的错误 If you shift+click or ctrl+click on a label for a checkbox, checkbox is not checked..

要使其正常工作,您需要在不使用 <label> 标记的情况下自定义您的复选框。

集成自定义复选框标签,效果很好。在最新的 Chrome 和 Firefox 中测试。

var lastCheckedRow = null;

$(document).on('click', '.mof-cell-checkbox', function handleRowCheckboxClick(e) {
  var $t = $(e.target);
  // check if target is checkbox, otherwise ignore event ( on label )
  if ($t.is(":checkbox")) {
    var $cb = $t.is(":checkbox") ? $t : $t.siblings("input.mof-cell-checkbox");
    // if not disabled
    if (!$cb.prop("disabled")) {
      if ($cb.hasClass("mof-cell-checkbox")) {
        if (window.getSelection().empty) {
          // Chrome
          window.getSelection().empty();
        }
        checkBoxClickHandler($cb, e.shiftKey);
      }
    }
  }
});

function checkBoxClickHandler($checkBoxInput, wasShiftPressed) {
  var isChecked = $checkBoxInput.prop("checked");
  $checkBoxInput
    .closest("table > tbody > tr")
    .toggleClass("row-selected", isChecked);

  if (
    lastCheckedRow &&
    lastCheckedRow.index() ==
      $checkBoxInput.closest("table > tbody > tr").index()
  ) {
    lastCheckedRow = null;
    return;
  }

  if (!lastCheckedRow) {
    lastCheckedRow = $checkBoxInput.closest("table > tbody > tr");
    return;
  }

  if (wasShiftPressed) {
    var start = $checkBoxInput.closest("table > tbody > tr").index();
    var end = lastCheckedRow.index();
    var $trs = $checkBoxInput
      .closest("table")
      .find("tbody > tr")
      .slice(Math.min(start, end), Math.max(start, end) + 1);
    if (
      lastCheckedRow.find(".mof-cell-checkbox").prop("checked") == isChecked
    ) {
      $trs.each(function () {
        $(this).toggleClass("row-selected", isChecked);
        $(this).find(".mof-cell-checkbox").prop("checked", isChecked);
      });
    } else {
      $trs.each(function () {
        $(this).toggleClass("row-selected", !isChecked);
        $(this).find(".mof-cell-checkbox").prop("checked", !isChecked);
      });
    }
  }

  lastCheckedRow = $checkBoxInput.closest("table > tbody > tr");
}
.custom-checkbox {
    width: 20px;
    height: 20px;
    position: relative;
}

.custom-checkbox > * {
    position: absolute;
}

.custom-checkbox-visible {
  width: 1.25rem;
  height: 1.25rem;
    margin: 2px;
    background-color: #dee2e6;
}

.custom-checkbox > input {
    z-index: 1;
    opacity: 0;
    left: 50%;
    top: 50%;
    transform: translatex(-50%) translatey(-50%);
    display: block;
    cursor: pointer;
    width: 20px;
    height: 20px;
  background-color: #dee2e6;
}

.custom-checkbox > input:checked + .custom-checkbox-visible {
    background: #2196f3;
  background-color: #2196f3;
    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
  background-position: center center;
  background-size: 50% 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
  <colgroup>
    <col width="30px">
  </colgroup>
  <tbody>
    <tr>
      <td>
        <div class="custom-checkbox custom-control">
          <input class="custom-control-input mof-cell-checkbox mof-cell-checkbox-item form-control" id="0__checkBox" type="checkbox" value="true">
          <div class="custom-checkbox-visible"></div>
        </div>
      </td>
      <td>1
      </td>
    </tr>
    <tr>
      <td>
        <div class="custom-checkbox custom-control">
          <input class="custom-control-input mof-cell-checkbox mof-cell-checkbox-item form-control" id="1__checkBox" type="checkbox" value="true"/>
          <div class="custom-checkbox-visible"></div>
        </div>
      </td>
      <td>2
      </td>
    </tr>
    <tr>
      <td>
        <div class="custom-checkbox custom-control">
          <input class="custom-control-input mof-cell-checkbox mof-cell-checkbox-item form-control" id="2__checkBox" type="checkbox" value="true">
          <div class="custom-checkbox-visible"></div>
        </div>
      </td>
      <td>3
      </td>
    </tr>
  </tbody>
</table>