HTML Проверка: Почему нельзя помещать интерактивный элемент внутрь интерактивного элемента? - PullRequest
4 голосов
/ 29 мая 2020

Отказ от ответственности: я понимаю, что это недействительно HTML. Я пытаюсь понять, почему это запрещено?

W3 C предполагает, что интерактивный элемент, такой как button или a, не должен содержать другой интерактивный элемент.

Я мог найти множество ресурсов, упоминающих это правило и некоторые обходные пути, а также некоторые ресурсы, связанные с тем, как это влияет на доступность и программы чтения с экрана, но почти все эти ресурсы говорят о том, что это требование, но не объясните почему.

https://adrianroselli.com/2016/12/be-wary-of-nesting-roles.html

https://codepen.io/vloux/pen/wXGyOv

Вложение внутри

https://github.com/dequelabs/axe-core/issues/601

Я не смог найти объяснения, почему это запрещено? приводит ли это к каким-либо проблемам с удобством использования?

Это связанный вопрос: Почему интерактивный элемент не должен использоваться в якоре?

Принятый ответ является удовлетворительным, но он недостаточно, чтобы сделать это правило обязательным. Описанной ситуации можно избежать, если правильно обработать событие.

Кроме того, если вложенный интерактивный контент недействителен, как мы должны иметь что-то вроде этого:

Карточка, на которую можно щелкнуть в целом, а также внутри нее есть интерактивный вторичный призыв к действию. Я знаю, что обходным путем было бы иметь в карточке первичный и вторичный призыв к действию, но разве не должно быть разрешено и вышеупомянутое?

Вот скрипка: https://jsfiddle.net/t9qbwas5/2/

<button type="button" class="card">
  The card itself is the primary CTA.
  <br/>
  <br/>
  some important content to read through.
  some important content to read through.
  some important content to read through.
  <div class="cta">
   Secondary CTA
  </div>
</button>

.cta {
  padding: 4px;
  background: #00a9a9;
  color: white;
  width: 80px;
  margin: auto;
  margin-top: 8px;
  margin-bottom: 8px;
}

.card {
  width: 200px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}

enter image description here В приведенном выше примере я добиваюсь этого, используя интерактивный div внутри кнопки, но это не semanti c (?), а также функционально, это интерактивный элемент внутри другого. Я пытаюсь понять, что даже если я использую этот обходной путь, принципиально неправильно иметь вложенные интерактивные элементы? это плохая практика дизайна / юзабилити?

Ответы [ 4 ]

4 голосов
/ 29 мая 2020

Ответ в принципе довольно прост. Когда вы щелкаете интерактивный элемент внутри другого интерактивного элемента, какую функцию вы должны запускать?

В вашем примере, если я нажимаю на вторичный CTA, должна ли она запускать функцию для вторичного CTA или должна запускать функцию для карты?

Скрипка ниже должна продемонстрировать проблему, перейдите к первой кнопке и нажмите введите , затем перейдите к CTA и нажмите Enter .

Очевидно, вы могли бы обойти это, но я думаю, что это демонстрирует суть.

$('.card').on('click', function(){
    console.log("card");
});
$('.cta').on('click', function(){
    console.log("cta");
});
.cta {
  padding: 4px;
  background: #00a9a9;
  color: white;
  width: 80px;
  margin: auto;
  margin-top: 8px;
  margin-bottom: 8px;
}

.card {
  width: 200px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" class="card">
  The card itself is the primary CTA.
  <br/>
  <br/>
  some important content to read through.
  some important content to read through.
  some important content to read through.
  <div class="cta" tabindex="0">
   Secondary CTA
  </div>
</button>

Затем этот принцип распространяется на устройства чтения с экрана и другие дополнительные и альтернативные устройства связи (AA C) .

Следует ли им учитывать родительский элемент при описании дочернего элемента? Должны ли они разрешать использование Пробел для активации, если вы вставляете флажок в <button>, должен ли Enter затем влиять только на кнопку или на оба?

1 голос
/ 29 мая 2020

Трудно ответить на вопросы «почему», потому что необходимо учитывать множество факторов, и, в конечном итоге, причина в том, что это указано в спецификации, но я попробую.

Когда такое поведение было spe c 'ed, этот стиль дизайна был не очень распространен. Ссылка обычно представляла собой одно изображение или небольшой фрагмент текста. Взгляните на ссылку на статью 2000 года :
]
Интерактивны только заголовок и изображение. Остальное - простой текст.

Даже сегодня это не так распространено. Также обратите внимание на страницу цен на Microsoft 365 :

Обратите внимание, что сама карта не интерактивна, а только элементы внутри нее. Вы можете увидеть основной призыв к действию «Купить сейчас» в виде кнопки и вторичный призыв к действию в виде гиперссылок.


Теперь о вашем примере: действительно ли эта карточка является кнопкой? Это может быть субъективно, но для меня это не кнопка. Кнопка обычно отображается с цветовой схемой, контрастирующей с окружающей страницей. Я бы сделал карту <div>, а вторичный CTA <button>.

Однако это может сбивать с толку пользователей, поскольку карта мне не кажется очень интерактивной. Подумайте о добавлении cursor: pointer к <div> (помимо всего необходимого для обеспечения доступности) `.


Я заметил, что вы отметили . Я думаю, что это не лучшая идея для людей, использующих программы чтения с экрана, и я думаю, что у большинства программ чтения с экрана возникнут проблемы с интерпретацией кнопки внутри кнопки (если бы браузер вообще это принимал).

Я бы использовал " Подход к странице цен на Microsoft 365 ». Это проще и хорошо работает с HTML.

1 голос
/ 29 мая 2020

Карточка, на которую можно щелкнуть в целом, а также внутри нее есть интерактивный дополнительный призыв к действию.

Хотя это визуально возможно и технически возможно, оно недоступно для вспомогательных технологий. , например программы для чтения с экрана

Давайте сделаем простой пример:

<button>
    Click for action 1
    <button>Click for action 2</button>
</button>

Доступное имя для первого <button> будет «Нажмите, чтобы выполнить действие 1 Нажмите, чтобы выполнить действие 2» . И если вы определите aria-label="Click for action 1", тогда внутренний элемент button не будет прочитан вообще.

Если вы действительно хотите сделать весь элемент доступным для кликов, вы можете идеально использовать javascript и по-прежнему быть доступными

<div class="outer">
  <button type="button" class="card">
    The card itself is the primary CTA.
  </button>
  <br/>
  <br/>
  some important content to read through.
  some important content to read through.
  some important content to read through.

  <button class="cta">
   Secondary CTA
  </button>
</div>

<script>
$(".outer").on("click", function() {$(".card").click()});
</script>

<style>
.outer {cursor: pointer}
</style>

В этом примере у вас будет правильно отображаться две кнопки для программ чтения с экрана: первая - «Сама карта является основным призывом к действию», а вторая - «Дополнительным призывом к действию», а щелчок мышью по всей карте приведет к тому же действию, что и первая кнопка.

1 голос
/ 29 мая 2020

Одна важная проблема связана с захватом событий; если вы щелкнете по интерактивному элементу, вложенному в другой интерактивный элемент (например, select внутри кликабельного button), здесь будет вмешательство, и могут произойти два случая, в зависимости от браузера;

случай 1: оба элемента вызовут это событие (например, click), событие

случай 2: родительский элемент захватит событие, а вложенный элемент не вызовет что event

на самом деле оба случая приведут к недетерминированному c поведению;

Фактически это не ограничивается click событиями, но click событиями осязаемее; Также программа чтения с экрана не сможет проанализировать разметку; взаимодействие с клавиатурой не будет работать должным образом; попробуйте в приведенном ниже фрагменте:

del.addEventListener('click', function(){
  console.log('deleting ...')
})

save.addEventListener('click', function(){
  console.log('saving ...')
})

sel.addEventListener('change', function(){
  console.log('changing to', sel.value)
})
<div id='del'>
delete 
<button id='save'> save 
<select id='sel'>
  <option>foo</option>
  <option>bar</option>
<select>
<input name='a' type='radio' />
<input name='a' type='radio' />
<input name='a' type='radio' />

</button>
</div>
...