ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

JavaScript: редактируемый div

Решил задачу «Редактируемый div» к подразделу 4.2 «Фокусировка: focus/blur» второй части учебника по JavaScript.

Задача не слишком сложная, но ее решение затруднено путаной (плохой) постановкой задачи. Цитата:

Создайте <div>, который превращается в <textarea>, если на него кликнуть.


Правильно это сформулировать надо было как-то так: «Создайте <div>, который с точки зрения пользователя превращается в <textarea>, если на него кликнуть». В программировании нет такого понятия, как «превращение», потому что программирование — это не магия.

После вышеописанного «превращения» по условиям задачи пользователь должен иметь возможность отредактировать содержимое HTML-элемента div. Окончание редактирования и обратное «превращение» HTML-элемента textarea в HTML-элемент div инициируется либо потерей фокуса HTML-элементом textarea, либо нажатием клавиши «Enter» на клавиатуре.

Что я подразумеваю под «содержимым» HTML-элемента div? Покажу в коде на языке HTML («содержимое» отметил красным цветом):
<div>содержимое</div>
То есть это то, что содержится между открывающим и закрывающим тегами HTML-элемента div. «Содержимым» может быть как обычный текст, так и код на языке HTML.

Демонстрацию решения задачи, сделанную ее авторами, можно посмотреть на отдельной странице.

Кроме этого, нам дана тестовая HTML-страница (в песочнице), к которой прилагается файл my.css со стилями HTML-элементов тестовой HTML-страницы.

На заданной HTML-странице уже есть тот самый HTML-элемент div с идентификатором view, который мы будем «превращать». Очевидно, что вместо «превращения» у нас будет два разных HTML-элемента — div и textarea, между которыми мы будем переключаться: в то время, как один из этих HTML-элементов будет виден, другой будет отсутствовать (не будет виден).

Чтобы получился эффект «превращения», эти два HTML-элемента должны иметь одинаковые размеры, что и обеспечивается стилями в вышеупомянутом файле my.css. На заданной HTML-странице целевой HTML-элемент div имеет CSS-класс .view, а HTML-элемент textarea, который мы создадим в своём скрипте, будет иметь CSS-класс .edit. Оба эти CSS-класса уже описаны в файле my.css.

* * *

Приступим к решению задачи. Вышеописанное «превращение», как сказано в постановке задачи, должно запуститься, когда пользователь кликнет мышкой на целевой HTML-элемент div. То есть мы должны повесить функцию-обработчик на соответствующее событие. Пишем код:
view.addEventListener("click", function () {
    //...
});

В этой функции-обработчике, по идее, первым делом нужно скрыть HTML-элемент div, чтобы вместо него показать HTML-элемент textarea. Сначала я решил сделать это с помощью свойства hidden HTML-элемента div. Но это не сработало, потому что в описании CSS-класса .view HTML-элемента div есть указание display: block;, а CSS-свойство display «перебивает» (по-английски «override») HTML-свойство hidden.

Поэтому я решил действовать через установку CSS-свойства display в самом HTML-элементе (при этом оно «перебьет» это же свойство, описанное в CSS-классе .view). Дополним код:
view.addEventListener("click", function () {
    // скрыть HTML-элемент div
    view.style.display = "none";

    //...
});

Далее нам нужно создать HTML-элемент textarea (на заданной HTML-странице такого HTML-элемента нет), задать ему соответствующий CSS-класс .edit (уже описанный в файле my.css) и передать в него «содержимое» целевого HTML-элемента div. Дополним код:
view.addEventListener("click", function () {
    // скрыть HTML-элемент div
    view.style.display = "none";

    // создать HTML-элемент textarea
    let textarea = document.createElement("textarea");
    textarea.classList.add("edit");  // CSS-класс
    textarea.value = view.innerHTML; // «содержимое»

    //...
});

Для создания эффекта «превращения» одного HTML-элемента в другой я решил вставить созданный HTML-элемент textarea после целевого HTML-элемента div. Инструкция view.style.display = "none"; не только делает HTML-элемент div невидимым, но еще заставляет браузер работать так, будто данного HTML-элемента div вообще нет на HTML-странице. Поэтому вставленный сразу после HTML-элемента div HTML-элемент textarea займет место HTML-элемента div на заданной HTML-странице. Дополним код:
view.addEventListener("click", function () {
    // скрыть HTML-элемент div
    view.style.display = "none";

    // создать HTML-элемент textarea
    let textarea = document.createElement("textarea");
    textarea.classList.add("edit");  // CSS-класс
    textarea.value = view.innerHTML; // «содержимое»

    // вставить HTML-элемент textarea после HTML-элемента div
    view.after(textarea);

    //...
});

Теперь отловим событие ухода фокуса с HTML-элемента textarea (например, это событие произойдет, если кликнуть мышкой за пределами HTML-элемента textarea). Для этого повесим функцию-обработчик на соответствующее событие. Дополним код:
view.addEventListener("click", function () {
    // скрыть HTML-элемент div
    view.style.display = "none";

    // создать HTML-элемент textarea
    let textarea = document.createElement("textarea");
    textarea.classList.add("edit");  // CSS-класс
    textarea.value = view.innerHTML; // «содержимое»

    // вставить HTML-элемент textarea после HTML-элемента div
    view.after(textarea);

    // при уходе фокуса с HTML-элемента textarea
    textarea.addEventListener("blur", function () {
        view.innerHTML = textarea.value; // обновить содержимое HTML-элемента div
        textarea.remove();               // удалить HTML-элемент textarea
        view.style.display = "";         // убрать стиль, скрывавший HTML-элемент div
    });

    //...
});

В этой функции-обработчике вернем отредактированное «содержимое» в HTML-элемент div, удалим теперь уже ненужный HTML-элемент textarea и сделаем HTML-элемент div видимым снова.

В принципе, это уже рабочее решение. Но по условиям задачи от нас требуется еще сделать то же самое, что при уходе фокуса с HTML-элемента textarea, и при нажатии в HTML-элементе textarea клавиши «Enter» на клавиатуре. Для этого я решил повесить функцию-обработчик на событие нажатия указанной клавиши, а в теле этой функции-обработчика вызвать метод ухода фокуса с HTML-элемента textarea. Таким образом, управление будет передано уже описанной функции-обработчику ухода фокуса. Дополним код:
view.addEventListener("click", function () {
    // скрыть HTML-элемент div
    view.style.display = "none";

    // создать HTML-элемент textarea
    let textarea = document.createElement("textarea");
    textarea.classList.add("edit");  // CSS-класс
    textarea.value = view.innerHTML; // «содержимое»

    // вставить HTML-элемент textarea после HTML-элемента div
    view.after(textarea);

    // при уходе фокуса с HTML-элемента textarea
    textarea.addEventListener("blur", function () {
        view.innerHTML = textarea.value; // обновить содержимое HTML-элемента div
        textarea.remove();               // удалить HTML-элемент textarea
        view.style.display = "";         // убрать стиль, скрывавший HTML-элемент div
    });

    // в случае нажатия клавиши Enter делаем то же самое, что и при уходе фокуса
    textarea.addEventListener("keydown", function (event) {
        if (event.code == "Enter") textarea.blur();
    });
});

Я посчитал этот код окончательным решением задачи, но после тестирования заметил, что при щелчке мышью на HTML-элемент div не возникает курсора после его «содержимого», то есть для начала редактирования мне нужно еще щелкнуть мышью в конец «содержимого» и только потом можно начинать редактировать это «содержимое».

Чтобы избавиться от этого недостатка, я решил установить фокус программно. Дополним код (красным цветом я пометил дополнение):
view.addEventListener("click", function () {
    // скрыть HTML-элемент div
    view.style.display = "none";

    // создать HTML-элемент textarea
    let textarea = document.createElement("textarea");
    textarea.classList.add("edit");  // CSS-класс
    textarea.value = view.innerHTML; // «содержимое»

    // вставить HTML-элемент textarea после HTML-элемента div
    view.after(textarea);

    // устанавим фокус на HTML-элемент textarea, чтобы можно было сразу набирать текст
    textarea.focus();

    // при уходе фокуса с HTML-элемента textarea
    textarea.addEventListener("blur", function () {
        view.innerHTML = textarea.value; // обновить содержимое HTML-элемента div
        textarea.remove();               // удалить HTML-элемент textarea
        view.style.display = "";         // убрать стиль, скрывавший HTML-элемент div
    });

    // в случае нажатия клавиши Enter делаем то же самое, что и при уходе фокуса
    textarea.addEventListener("keydown", function (event) {
        if (event.code == "Enter") textarea.blur();
    });
});

Это окончательный вариант скрипта.
Tags: Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 10 comments