Пользовательский флажок не будет переключаться при нажатии и перетаскивании на ярлык [Решено] - PullRequest
3 голосов
/ 19 апреля 2020

У меня проблема с моим пользовательским флажком.

Если вы щелкнете по элементу флажка, переместите курсор мыши, а затем отпустите щелчок внутри области флажка, флажок будет установлен.

Однако, если вы сделаете то же самое для пользовательского флажка (здесь div внутри метки), флажок не установлен.

Это проблема, потому что если вы хотите быстро установить флажок, вы можете двигайте мышь после нажатия кнопки мыши и до ее отпускания, чтобы не устанавливать флажок.

Пользователь обязан щелкать, не перемещая мышь.

Я знаю, что могу использовать JS для эмуляции флажка с div, но я хочу, чтобы HTML был семантически правильным, поэтому: можно ли исправить это без js?

Вот код:

/* 1. Hide the checkbox */

      .hidden {
        /* https://zellwk.com/blog/hide-content-accessibly/ */
        border: 0;
        clip: rect(0, 0, 0, 0);
        height: auto;
        margin: 0;
        overflow: hidden;
        padding: 0;
        position: absolute;
        width: 1px;
        white-space: nowrap;
      }

/* 2. Use a label to retrieve the click event */

      label {
        /* not used directly to prevent the bug in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=608180 */
        pointer-events: none;
        display: inline-flex;
      }

      label > input {
        /*usefull for testing only*/
        pointer-events : all;
      }

      label > .customCheckbox {
        cursor: pointer;
        display: inline-flex;
        justify-content: center;
        align-items: center;
        position: relative;

        --size:200px;
        width: var(--size);
        height: var(--size);
        font-size: calc(var(--size)/2);
        pointer-events: all;
      }

      label > .customCheckbox::selection{
      /* prevent highliting the text within the custom checkbox */
          background: none;
      }

      label > .customCheckbox:after {
        
        z-index: -1;
        content: "✔";
        display: inline-flex;
        justify-content: center;
        align-items: center;

        position: absolute;
        width: 100%;
        height: 100%;
        
        box-sizing: border-box;
        border-radius: calc(var(--size)/4);
        border: solid black calc(var(--size)/10);
      }

      label > input:not(:checked) + div.customCheckbox:after {
        background:#0000;
        content: "\a0";
      }
<input type="checkbox"/>   

<label >
        <input type="checkbox" class="hidden"/>
        <div class="customCheckbox"></div>
</label >

Мне нужно, чтобы флажок был внутри ярлыка, потому что я не могу использовать атрибут "for".

Большое спасибо за ваш help!

EDIT: для тех, кому интересно, вот решение js (не идеально, но так как я не могу сделать это в CSS, оно лучше, чем ничего):

let checkboxes = document.getElementsByClassName("customCheckbox");

    for(let i=0; i<checkboxes.length; i++){
      checkboxes[i].addEventListener('mouseover', (e)=>{handleMouseOverCheckbox(e)})
      checkboxes[i].addEventListener('mousedown', (e)=>{handleMouseOverCheckbox(e)})
    }

    function handleMouseOverCheckbox(e) {
      e.srcElement.previousElementSibling.disabled = "true";
      if (e.buttons == 1) {
        e.srcElement.previousElementSibling.checked= !e.srcElement.previousElementSibling.checked;
      }}

РЕДАКТИРОВАТЬ 2: Вот лучшее решение, которое я мог придумать, благодаря @ zer00ne

codepen.io / DesignThinkerer / pen / bGVBLjM

1 Ответ

1 голос
/ 21 апреля 2020

Флажок внутри метки не проблема. Проблема возникает, когда этот флажок изменен ради доступности вместо полного удаления с помощью display: none. Если в DOM существует такой интерактивный элемент, как ввод или кнопка, он все равно будет иметь значение, независимо от того, как он скрыт, если не будет применено display: none.

В O riginal P ost флажок практически невозможно щелкнуть из-за его высоты в 0px и ширины в 1px, и все же при нажатии div, флажок щелкнул ... иногда нет. Обычно, если метка способна обнаружить щелчок, этот щелчок вызовет событие щелчка и для вложенного флажка. В коде OP этого не происходит, потому что у метки pointer-events: none.

Значит, div получает щелчок, и каким-то волшебным чудом он получает функции, которые обычно не приписывают ему? Div не являются интерактивными, они не могут влиять на элементы, которые не вложены в себя (например, как флажок, расположенный перед div). Нет, div бесполезен, это сам флажок, на который щелкают, потому что это единственный элемент в инертной метке, который получает фокус по умолчанию. Получение фокуса на входе не обязательно гарантирует событие щелчка - фактически событие фокуса выбирает элемент, а событие щелчка устанавливает элемент как активный. Так что же происходит, когда пользователь дважды щелкает или быстро перемещает мышь, прежде чем следующий щелчок очистит ярлык? Нежелательное поведение, как описано в OP.

В следующей демонстрации флажки скрыты в соответствии с OP (также установите ширину и высоту равными 0), удалите pointer-events: none из метки и добавьте его в флажки. В этой настройке метка получает фокус и события щелчка, а событие щелчка вызывает флажок. Флажок, который был изолирован от любых дополнительных кликов из-за pointer-events: none и z-index: -1, должен работать как положено.

В качестве подтверждения концепции я добавил JavaScript, чтобы продемонстрировать стабильность кода. Два обработчика событий предназначены для демонстрационных целей. JS не облегчает, не стабилизирует и не изменяет производительность поведения HTML / CSS.

  1. При любом событии изменения в флажке (через метку) будет срабатывать function changeHandler() собрать все значения отмеченных флажков и отобразить их в выводе.

    • Если в поле есть флажок и отображается значение, соответствующее этому флажку, то оно успешно переходит в правильное состояние.
  2. Нажав button.show, вы включите function clickHandler() для переключения класса .reveal на каждый флажок.

    • Быстро нажимая, обратите внимание, что установленные флажки отмечены, а также соответствующая пользовательская метка. Также обратите внимание, что значение также должно отображаться.

BTW

  • " .... target не работает в IE IIR C "

    event.target - это стандартное свойство, используемое в каждом современном браузере. event.srcElement является устаревшим свойством, используемым IE, которое почти полностью не поддерживается.

  • pointer-events: all присвоено input и .customCheckbox

    Значение all относится только к SVG. Только значения none и auto относятся к HTML. auto по умолчанию.


Демо

Я не могу воспроизвести описанное поведение, кроме как в коде, представленном в OP. Если вы можете воспроизвести это поведение в моей демоверсии, запишите короткое видео и опубликуйте его, а также машину / устройство, ОС и браузеры (я буду считать, что все достаточно актуально).

const main = document.forms.main;

main.onchange = checkHandler;

function checkHandler(e) {
  const fc = this.elements;
  const chx = [...fc.hidden];
  const ui = e.target;

  if (ui.matches('.hidden')) {
    let text = chx.flatMap(c => c.checked ? [c.value] : []);
    fc.view.value = '';
    fc.view.value = text.join(', ');
  }
}

main.onclick = clickHandler

function clickHandler(e) {
  const fc = this.elements;
  const chx = [...fc.hidden];
  const ui = e.target;

  if (ui.matches('button.show')) {
    chx.forEach(c => c.classList.toggle('reveal'));
  }
}
:root,
body {
  --size: 10rem;
  font: 400 small-caps 2vw/1.5 Times;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}

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

.display {
  display: flex;
  flex-flow: row nowrap;
  justify-content: space-between;
  align-items: center;
  width: max-content;
  max-height: min-content;
  margin: 10px;
  border: calc(var(--size) / 20) solid #000;
  border-radius: 24px;
}

.view {
  display: inline-block;
  min-width: 35ch;
  font-size: 1.5rem;
  height: 1.5rem;
  line-height: 1;
}

.show {
  display: inline-block;
  width: 12ch;
  padding: 1px 3px;
  margin: 4px;
  border: 2px solid #000;
  border-radius: 8px;
  background: none;
  text-align: center;
  font-size: 1.25rem;
  cursor: pointer;
}

.mask {
  position: relative;
  z-index: 2;
  display: block;
  width: var(--size);
  height: var(--size);
  padding: 0;
  margin: 0 5px;
  border: solid black calc(var(--size) / 10);
  border-radius: calc(var(--size) / 4);
  font-size: calc(var(--size) * 0.8);
  background: none;
  cursor: pointer;
}

.icon {
  position: absolute;
  z-index: 1;
  top: -1.5rem;
  right: 1rem;
  display: inline-block;
  margin: 0;
  padding: 0;
  border: 0;
}

.hidden {
  position: relative;
  z-index: -1;
  display: inline-block;
  width: 0;
  height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
}

.reveal {
  z-index: 0;
  top: -24px;
  left: 4px;
  width: 1px;
  height: 1px;
  opacity: 1;
}

.icon::after {
  content: attr(data-blank);
}

.hidden:checked+.icon::after {
  content: attr(data-check);
}
<!DOCTYPE html>
<html>

<head lang='en'>
  <meta charset='utf-8'>
  <style></style>
</head>

<body>
  <form name='main'>
    <fieldset name='display' class='display'>
      <output name='view' class='view'></output>
      <button name='show' class='show' type='button'>Show</button>
    </fieldset>
    <fieldset name='display' class='display'>
      <label name='mask' class='mask'>
    <input name='hidden' class="hidden" type="checkbox" value='Check I'>
    <fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
  </label>


      <label name='mask' class='mask'>
    <input name='hidden' class="hidden" type="checkbox" value='Check II'>
    <fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
  </label>


      <label name='mask' class='mask'>
    <input name='hidden' class="hidden" type="checkbox" value='Check III'>
    <fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
  </label>


      <label name='mask' class='mask'>
    <input name='hidden' class="hidden" type="checkbox" value='Check IV'>
    <fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
  </label>
    </fieldset>
  </form>

  <script></script>
</body>

</html>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...