Почему не редактируется поле вручную javascript
Перейти к содержимому

Почему не редактируется поле вручную javascript

  • автор:

Редактирование отдельного элемента на JavaScript

Сейчас мы с вами научимся редактировать тексты элементов с помощью полей ввода. Начнем с простых вещей и будем постепенно усложнять.

Итак, пусть у нас даны абзац и инпут, расположенные в одном родителе:

Давайте сделаем так, чтобы по потери фокуса в инпуте его текст появлялся в абзаце:

let elem = document.querySelector(‘#elem’); let input = document.querySelector(‘#input’); input.addEventListener(‘blur’, function() < elem.textContent = this.value; >);

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

let elem = document.querySelector(‘#elem’); let input = document.querySelector(‘#input’); input.value = elem.textContent; // записываем в инпут текст абзаца input.addEventListener(‘blur’, function() < elem.textContent = this.value; >);

Модифицируйте приведенный выше код так, чтобы текст абзаца менялся не по потери фокуса, а по мере ввода текста в инпут.

Появление инпута

Давайте теперь сделаем так, чтобы инпута изначально не было на странице, а он появлялся по клику на абзац. То есть наш начальный HTML будет выглядеть так:

Для начала просто реализуем появление инпута, без редактирования:

let elem = document.querySelector(‘#elem’); elem.addEventListener(‘click’, function() < let input = document.createElement('input'); input.value = elem.textContent; elem.parentElement.appendChild(input); >);

А теперь давайте сделаем так, чтобы по потери фокуса в инпуте менялся текст абзаца:

Наш код, однако, несовершенен, так как каждое нажатие на абзац будет приводить к появлению нового инпута.

Для решения проблемы просто будем по потери фокуса удалять текущий инпут:

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

Нет доступа к value

в чем конкретно проблема? что за элемент inputText? почему решили, что не можете получить value? Куда нужно вставить строку el.innerHTML = inputT.value;, предыдущий код заработал как надо?

21 июл 2016 в 12:06

Я дополнил код и оформил его так что бы можно было его запустить. Проверьте так ли работает код как должен

21 июл 2016 в 20:42

3 ответа 3

Сортировка: Сброс на вариант по умолчанию

Проблема с кодом в том, что значение поля value — Это строка и получив ее один раз, она не будет меняться самостоятельно каждый раз как меняется значение в поле ввода.

Так как до нажатия на кнопку значение поля может меняться, единственный способ получить текущее значение поля ввода во время клика — взять его непосредственно из инпута в обработчике.

Отслеживать
ответ дан 21 июл 2016 в 19:18
81.1k 9 9 золотых знаков 78 78 серебряных знаков 135 135 бронзовых знаков
Как вариант — использовать MutationObserver и обновлять переменную при изменении.
– user207618
11 авг 2016 в 18:09
@Other, да, может быть, не видно какой-то особой выгоды от этого
11 авг 2016 в 18:12

Быть может. Затраты на наблюдателя могут быть больше, чем на геттер элемента. Но как экзотика — почему нет?

Фокусировка: focus/blur

Элемент получает фокус, когда пользователь кликает по нему или использует клавишу Tab . Также существует HTML-атрибут autofocus , который устанавливает фокус на элемент, когда страница загружается. Есть и другие способы получения фокуса, о них – далее.

Фокусировка обычно означает: «приготовься к вводу данных на этом элементе», это хороший момент, чтобы инициализовать или загрузить что-нибудь.

Момент потери фокуса («blur») может быть важнее. Это момент, когда пользователь кликает куда-то ещё или нажимает Tab , чтобы переключиться на следующее поле формы. Есть другие причины потери фокуса, о них – далее.

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

В работе с событиями фокусировки есть важные особенности. Мы постараемся разобрать их далее.

События focus/blur

Событие focus вызывается в момент фокусировки, а blur – когда элемент теряет фокус.

Используем их для валидации(проверки) введённых данных.

  • Обработчик blur проверяет, введён ли email, и если нет – показывает ошибку.
  • Обработчик focus скрывает это сообщение об ошибке (в момент потери фокуса проверка повторится):
 .invalid < border-color: red; >#error Ваш email: >; input.onfocus = function() < if (this.classList.contains('invalid')) < // удаляем индикатор ошибки, т.к. пользователь хочет ввести данные заново this.classList.remove('invalid'); error.innerHTML = ""; >>; 

Современный HTML позволяет делать валидацию с помощью атрибутов required , pattern и т.д. Иногда – это всё, что нам нужно. JavaScript можно использовать, когда мы хотим больше гибкости. А ещё мы могли бы отправлять изменённое значение на сервер, если оно правильное.

Методы focus/blur

Методы elem.focus() и elem.blur() устанавливают/снимают фокус.

Например, запретим посетителю переключаться с поля ввода, если введённое значение не прошло валидацию:

 .error Ваш email:   

Это сработает во всех браузерах, кроме Firefox (bug).

Если мы что-нибудь введём и нажмём Tab или кликнем в другое место, тогда onblur вернёт фокус обратно.

Отметим, что мы не можем «отменить потерю фокуса», вызвав event.preventDefault() в обработчике onblur потому, что onblur срабатывает после потери фокуса элементом.

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

Потеря фокуса, вызванная JavaScript

Потеря фокуса может произойти по множеству причин.

Одна из них – когда посетитель кликает куда-то ещё. Но и JavaScript может быть причиной, например:

  • alert переводит фокус на себя – элемент теряет фокус (событие blur ), а когда alert закрывается – элемент получает фокус обратно (событие focus ).
  • Если элемент удалить из DOM, фокус также будет потерян. Если элемент добавить обратно, то фокус не вернётся.

Из-за этих особенностей обработчики focus/blur могут сработать тогда, когда это не требуется.

Используя эти события, нужно быть осторожным. Если мы хотим отследить потерю фокуса, которую инициировал пользователь, тогда нам следует избегать её самим.

Включаем фокусировку на любом элементе: tabindex

Многие элементы по умолчанию не поддерживают фокусировку.

Какие именно – зависит от браузера, но одно всегда верно: поддержка focus/blur гарантирована для элементов, с которыми посетитель может взаимодействовать: , , , и т.д.

Это можно изменить HTML-атрибутом tabindex .

Любой элемент поддерживает фокусировку, если имеет tabindex . Значение этого атрибута – порядковый номер элемента, когда клавиша Tab (или что-то аналогичное) используется для переключения между элементами.

То есть: если у нас два элемента, первый имеет tabindex=»1″ , а второй tabindex=»2″ , то находясь в первом элементе и нажав Tab – мы переместимся во второй.

Порядок перебора таков: сначала идут элементы со значениями tabindex от 1 и выше, в порядке tabindex , а затем элементы без tabindex (например, обычный ).

При совпадающих tabindex элементы перебираются в том порядке, в котором идут в документе.

Есть два специальных значения:

  • tabindex=»0″ ставит элемент в один ряд с элементами без tabindex . То есть, при переключении такие элементы будут после элементов с tabindex ≥ 1 . Обычно используется, чтобы включить фокусировку на элементе, но не менять порядок переключения. Чтобы элемент мог участвовать в форме наравне с обычными .
  • tabindex=»-1″ позволяет фокусироваться на элементе только программно. Клавиша Tab проигнорирует такой элемент, но метод elem.focus() будет действовать.

Например, список ниже. Кликните первый пункт в списке и нажмите Tab :

    Один Ноль Два Минус один

Перебираемые объекты

Перебираемые (или итерируемые) объекты – это обобщение массивов. Концепция, которая позволяет использовать любой объект в цикле for..of .

Конечно же, сами массивы являются перебираемыми объектами. Но есть и много других встроенных перебираемых объектов, например, строки.

Если объект не является массивом, но представляет собой коллекцию каких-то элементов (список, набор), то удобно использовать цикл for..of для их перебора, так что давайте посмотрим, как это сделать.

Symbol.iterator

Мы легко поймём принцип устройства перебираемых объектов, создав один из них.

Например, у нас есть объект. Это не массив, но он выглядит подходящим для for..of .

Например, объект range , который представляет собой диапазон чисел:

let range = < from: 1, to: 5 >; // Мы хотим, чтобы работал for..of: // for(let num of range) . num=1,2,3,4,5

Чтобы сделать range итерируемым (и позволить for..of работать с ним), нам нужно добавить в объект метод с именем Symbol.iterator (специальный встроенный Symbol , созданный как раз для этого).

  1. Когда цикл for..of запускается, он вызывает этот метод один раз (или выдаёт ошибку, если метод не найден). Этот метод должен вернуть итератор – объект с методом next .
  2. Дальше for..of работает только с этим возвращённым объектом.
  3. Когда for..of хочет получить следующее значение, он вызывает метод next() этого объекта.
  4. Результат вызова next() должен иметь вид , где done=true означает, что цикл завершён, в противном случае value содержит очередное значение.

Вот полная реализация range с пояснениями:

let range = < from: 1, to: 5 >; // 1. вызов for..of сначала вызывает эту функцию range[Symbol.iterator] = function() < // . она возвращает объект итератора: // 2. Далее, for..of работает только с этим итератором, // запрашивая у него новые значения return < current: this.from, last: this.to, // 3. next() вызывается на каждой итерации цикла for..of next() < // 4. он должен вернуть значение в виде объекта if (this.current ; > else < return < done: true >; > > >; >; // теперь работает! for (let num of range) < alert(num); // 1, затем 2, 3, 4, 5 >

Обратите внимание на ключевую особенность итераторов: разделение ответственности.

  • У самого range нет метода next() .
  • Вместо этого другой объект, так называемый «итератор», создаётся вызовом range[Symbol.iterator]() , и именно его next() генерирует значения.

Таким образом, объект итератор отделён от самого итерируемого объекта.

Технически мы можем объединить их и использовать сам range как итератор, чтобы упростить код.

Например, вот так:

let range = < from: 1, to: 5, [Symbol.iterator]() < this.current = this.from; return this; >, next() < if (this.current ; > else < return < done: true >; > > >; for (let num of range) < alert(num); // 1, затем 2, 3, 4, 5 >

Теперь range[Symbol.iterator]() возвращает сам объект range : у него есть необходимый метод next() , и он запоминает текущее состояние итерации в this.current . Короче? Да. И иногда такой способ тоже хорош.

Недостаток такого подхода в том, что теперь мы не можем использовать этот объект в двух параллельных циклах for..of : у них будет общее текущее состояние итерации, потому что теперь существует лишь один итератор – сам объект. Но необходимость в двух циклах for..of , выполняемых одновременно, возникает редко, даже при наличии асинхронных операций.

Бесконечные итераторы

Можно сделать бесконечный итератор. Например, range будет бесконечным при range.to = Infinity . Или мы можем создать итерируемый объект, который генерирует бесконечную последовательность псевдослучайных чисел. Это бывает полезно.

Метод next не имеет ограничений, он может возвращать всё новые и новые значения, это нормально.

Конечно же, цикл for..of с таким итерируемым объектом будет бесконечным. Но мы всегда можем прервать его, используя break .

Строка – перебираемый объект

Среди встроенных перебираемых объектов наиболее широко используются массивы и строки.

Для строки for..of перебирает символы:

for (let char of "test") < // срабатывает 4 раза: по одному для каждого символа alert( char ); // t, затем e, затем s, затем t >

И он работает корректно даже с суррогатными парами!

let str = '����'; for (let char of str) < alert( char ); // ��, а затем �� >

Явный вызов итератора

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

Мы будем перебирать строку точно так же, как цикл for..of , но вручную, прямыми вызовами. Нижеприведённый код получает строковый итератор и берёт из него значения:

let str = "Hello"; // делает то же самое, что и // for (let char of str) alert(char); let iterator = str[Symbol.iterator](); while (true) < let result = iterator.next(); if (result.done) break; alert(result.value); // выводит символы один за другим >

Такое редко бывает необходимо, но это даёт нам больше контроля над процессом, чем for..of . Например, мы можем разбить процесс итерации на части: перебрать немного элементов, затем остановиться, сделать что-то ещё и потом продолжить.

Итерируемые объекты и псевдомассивы

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

  • Итерируемые объекты – это объекты, которые реализуют метод Symbol.iterator , как было описано выше.
  • Псевдомассивы – это объекты, у которых есть индексы и свойство length , то есть, они выглядят как массивы.

При использовании JavaScript в браузере или других окружениях мы можем встретить объекты, которые являются итерируемыми или псевдомассивами, или и тем, и другим.

Например, строки итерируемы (для них работает for..of ) и являются псевдомассивами (они индексированы и есть length ).

Но итерируемый объект может не быть псевдомассивом. И наоборот: псевдомассив может не быть итерируемым.

Например, объект range из примера выше – итерируемый, но не является псевдомассивом, потому что у него нет индексированных свойств и length .

А вот объект, который является псевдомассивом, но его нельзя итерировать:

let arrayLike = < // есть индексы и свойство length =>псевдомассив 0: "Hello", 1: "World", length: 2 >; // Ошибка (отсутствует Symbol.iterator) for (let item of arrayLike) <>

Что у них общего? И итерируемые объекты, и псевдомассивы – это обычно не массивы, у них нет методов push , pop и т.д. Довольно неудобно, если у нас есть такой объект и мы хотим работать с ним как с массивом. Например, мы хотели бы работать с range , используя методы массивов. Как этого достичь?

Array.from

Есть универсальный метод Array.from, который принимает итерируемый объект или псевдомассив и делает из него «настоящий» Array . После этого мы уже можем использовать методы массивов.

let arrayLike = < 0: "Hello", 1: "World", length: 2 >; let arr = Array.from(arrayLike); // (*) alert(arr.pop()); // World (метод работает)

Array.from в строке (*) принимает объект, проверяет, является ли он итерируемым объектом или псевдомассивом, затем создаёт новый массив и копирует туда все элементы.

То же самое происходит с итерируемым объектом:

// range взят из примера в начале статьи let arr = Array.from(range); alert(arr); // 1,2,3,4,5 (преобразование массива через toString работает)

Полный синтаксис Array.from позволяет указать необязательную «трансформирующую» функцию:

Array.from(obj[, mapFn, thisArg])

Необязательный второй аргумент может быть функцией, которая будет применена к каждому элементу перед добавлением в массив, а thisArg позволяет установить this для этой функции.

// range взят из примера в начале статьи // возводим каждое число в квадрат let arr = Array.from(range, num => num * num); alert(arr); // 1,4,9,16,25

Здесь мы используем Array.from , чтобы превратить строку в массив её элементов:

let str = '����'; // разбивает строку на массив её элементов let chars = Array.from(str); alert(chars[0]); // �� alert(chars[1]); // �� alert(chars.length); // 2

В отличие от str.split , этот метод в работе опирается на итерируемость строки, и поэтому, как и for..of , он корректно работает с суррогатными парами.

Технически это то же самое, что и:

let str = '����'; let chars = []; // Array.from внутри себя выполняет тот же цикл for (let char of str) < chars.push(char); >alert(chars);

…Но гораздо короче.

Мы можем даже создать slice , который поддерживает суррогатные пары:

function slice(str, start, end) < return Array.from(str).slice(start, end).join(''); >let str = '������'; alert( slice(str, 1, 3) ); // ���� // а вот встроенный метод не поддерживает суррогатные пары alert( str.slice(1, 3) ); // мусор (две части различных суррогатных пар)

Итого

Объекты, которые можно использовать в цикле for..of , называются итерируемыми.

  • Технически итерируемые объекты должны иметь метод Symbol.iterator .
    • Результат вызова obj[Symbol.iterator] называется итератором. Он управляет процессом итерации.
    • Итератор должен иметь метод next() , который возвращает объект , где done:true сигнализирует об окончании процесса итерации, в противном случае value – следующее значение.

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

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

    Array.from(obj[, mapFn, thisArg]) создаёт настоящий Array из итерируемого объекта или псевдомассива obj , и затем мы можем применять к нему методы массивов. Необязательные аргументы mapFn и thisArg позволяют применять функцию с задаваемым контекстом к каждому элементу.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *