Как перехватить событие «keydown» элементом, унаследовавшим атрибут contenteditable?

Для вступления я хотел бы объяснить, чего я пытаюсь достичь:

Внутри *div#content* я хотел бы обнаружить keydown события И получить элемент, для которого обнаружен элемент нажатия клавиши. Например, TAB внутри абзаца «Программирование — это весело». должен вернуть элемент абзаца и нажать клавишу TAB.

//javascript
const buttonEditable = document.getElementById("buttonEditable");
const contentDiv = document.getElementById("content");
const paragraph = document.getElementById('paragraph');
paragraph.addEventListener('keydown',keydown);

function keydown(){
    console.info(event.key); // should return TAB
    console.info(event.target); //should return paragraph element
}

buttonEditable.addEventListener('click',toggleEditable);

function toggleEditable(){
    if (contentDiv.contentEditable === 'false'){
        contentDiv.contentEditable = true;
    } else {
        contentDiv.contentEditable = false;
    };
}
<!-- HTML -->

<div id = "content"> <!-- div container keeps HTML element which are toggled from editable to non-editable-->
    <p id = "paragraph">Coding is fun.</p>
    <!-- ...bunch of further html element-->
</div>

<button id = "buttonEditable">toggleEditable</button>

Я провел следующее исследование: события «keydown» обнаруживаются только для элементов, имеющих атрибут contenteditable = true. Кстати, события «щелчка» обнаруживаются независимо от того, установлен ли атрибут contentEditable.

Добавив contenteditable = true в html-код. Это работает... Обнаружено нажатие клавиши TAB внутри абзаца:

 <div id = "content">
    <p id = "paragraph" contenteditable = "true">Coding is fun.</p> <!-- <<<<<<<<<<-------contenteditable added -->
</div>

<button id = "buttonEditable">toggleEditable</button>

Кто-нибудь знает способ, как элемент AND клавиши TAB можно обнаружить по абзацу, который наследует contenteditable = "true"? Конечно, с помощью селектора запросов я мог бы назначить атрибут присваивания contenteditable любому элементу внутри div#content, но мне это кажется не очень элегантным.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

... прецедент ...

function handleKeydown(evt) {
  console.info(evt.key);
  console.info(evt.target);
}
function toggleTopMostContenteditable () {
  const elm = document.querySelector('#content');

  elm.contentEditable = (elm.contentEditable !== 'true');
}
document
  .querySelectorAll('#content, #content > p')
  .forEach(elm =>
    elm.addEventListener('keydown', handleKeydown)
  );
document
  .querySelector('button')
  .addEventListener('click', toggleTopMostContenteditable);
body { margin: 0;}
p { margin: 5px; }

#content {
  width: 30.7%;
  margin: 0 0 10px 0;
  padding: 5px;

  border: 1px dashed red;
  
  &[contenteditable = "true"],
  [contenteditable = "true"] {
    
    border: 1px solid green;
  }
}
.as-console-wrapper {
  left: auto!important;
  bottom: 0;
  width: 67%;
  min-height: 100%;
}
<div id = "content" contenteditable = "true">
  <p contenteditable = "true">Coding is fun.</p>
  <p contenteditable = "true">The quick brown fox ...</p>
  <p contenteditable = "true">... jumps over the lazy dog.</p>
</div>

<button>Toggle top-most contenteditable</button>

Чтобы достичь того, чего хочет OP, необходимо подделать состояние contentEditable родительского элемента вместе с реализацией, которая обрабатывает состояния contentEditable, специфичные для дочернего элемента.

Тоже что-то похожее...

function handleTargetSpecificKeydown(evt) {
  console.info(evt.key);    // - should return TAB
  console.info(evt.target); // - should return paragraph element
}
function handleTargetSpecificEditableState({ target }) {
  if (!target.matches('#content')) {

    const recentlyEditable = target.contentEditable === 'true';
    const currentlyEditable =
      (target.closest('#content').dataset.contenteditable === 'true');

    if (!recentlyEditable && currentlyEditable) {

      target.addEventListener('keydown', handleTargetSpecificKeydown);
      target.contentEditable = true;
      target.focus();

    } else if (recentlyEditable && !currentlyEditable) {

      target.contentEditable = false;
      target.removeEventListener('keydown', handleTargetSpecificKeydown);
    }
  }
  console.info({
    target,
    'target.contentEditable': target.contentEditable,
  });
}

function toggleOverallEditableState () {
  const { dataset } = contentDiv;

  dataset.contenteditable = (dataset.contenteditable !== 'true');

  console.info({ contentDiv });
}
const contentDiv = document.querySelector('#content');

contentDiv
  .addEventListener('click', handleTargetSpecificEditableState);

document
  .querySelector('button')
  .addEventListener('click', toggleOverallEditableState);
body { margin: 0;}
p { margin: 5px; }

#content {
  width: 30.7%;
  margin: 0 0 10px 0;
  padding: 5px;

  p {
    border: 1px dashed red;
  }
  border: 1px dashed red;
  
  &[data-contenteditable = "true"],
  [contenteditable = "true"] {
    
    border: 1px solid green;
  }
}
.as-console-wrapper {
  left: auto!important;
  bottom: 0;
  width: 67%;
  min-height: 100%;
}
<div id = "content">
  <p>Coding is fun.</p>
  <p>The quick brown fox ...</p>
  <p>... jumps over the lazy dog.</p>
</div>

<button>Toggle overeall editable state</button>

Другие вопросы по теме