Мне трудно понять, почему добавление TEXT_NODE (document.createTextNode('some string')
) в существующий родительский элемент не обновляет количество элементов при использовании цикла for.
Не поймите меня неправильно, это упрощает изменение родительского элемента, я просто не понимаю, почему длина массива не обновляется.
Я думал, что при работе с такого рода элементами они «живые», т.е. обновление с внесенными изменениями.
Редактировать №1:
Почему const words = topNode.textContent.split(" ");
не обновляется при добавлении новых строк в элемент topNode
-s. т.е.: в начале длина равна 2 (0 - один, 1 - два, 2 - три), при добавлении "какой-то строки" я ожидаю, что длина words
будет равна 4 (0 - один, 1 - два, 2 - некоторые, 3 – струна, 4 – три).
Редактировать № 2:
Я хотел бы знать, почему длина массива не обновляется, поскольку я знаю, что в некоторых случаях (я не знаю, в каких случаях...) длина массива обновляется при внесении изменений. Почему в некоторых случаях, а не в этом случае?
Пример:
const textarea = document.querySelector("#textarea");
const topNodes = textarea.childNodes;
for (const topNode of topNodes){
if (topNode.nodeType === 3){
let charCount = 0; //character count includes the number of letters and spaces - used for appending a word - function appendString
const words = topNode.textContent.split(" ");
for (let i = 0; i < words.length - 1; i++) {
const word = words[i];
if (typeof word !== 'undefined' && !word.length){
charCount++; //in case of double space (currently irrelevant)
continue;
}
charCount += word.length + 1; //adding + 1 as words are split by space
if (word === 'two'){
appendString(charCount, topNode, 'some string');
}
console.info("+-------------------------+");
console.info("| current word count: ", words.length - 1, " |")
console.info("| actual word count: ", topNode.textContent.split(" ").length - 1, " |")
}
}
}
function appendString(charCount, thisParentNode, string){
const nodeText = thisParentNode.textContent;
const before = nodeText.slice(0, charCount);
const after = nodeText.slice(charCount);
thisParentNode.textContent = before;
if (string.charAt(string.length - 1) !== " "){
string = string + " ";
}
const textNodeAfter = document.createTextNode(after);
const textNodeAppend = document.createTextNode(string);
thisParentNode.parentNode.insertBefore(textNodeAppend, thisParentNode.nextSibling);
thisParentNode.parentNode.insertBefore(textNodeAfter, textNodeAppend.nextSibling);
//and normalize all the way up to textarea
const textarea = document.querySelector("#textarea");
normalizeAll(thisParentNode, textarea);
function normalizeAll(thisNode, topNode){
if (thisNode !== topNode){
thisNode.normalize();
normalizeAll(thisNode.parentNode, topNode);
} else {
thisNode.normalize();
}
}
}
#body{
height: 100%
}
#textarea {
min-height: 30%;
border: 1px solid black;
}
<html>
<body>
<div id = "textarea" contenteditable>one two three</div>
</body>
</html>
Чтобы дополнительно объяснить, почему это на самом деле «проблема», возьмем пример ниже. Если я хочу добавить к элементу несколько строк, мне нужно количество символов (чтобы они были добавлены в правильное место).
Если мы возьмем переменную topNode
— которая является элементом TEXT_NODE; и мы хотим добавить к элементу несколько строк (textNodes), я ожидаю, что добавление строки после первого раза (2cnd, 3rd, 4th и т. д.) будет добавлено в неправильном месте.
Пример:
один два три два четыре два пять
номер петли. 2
charCount должно быть -> 7 (один два)
charCount на самом деле -> 7
вставь "что-нибудь"
номер петли. 4
charCount должно быть -> 27 (один два что-то три два)
charCount на самом деле -> 17 (один два три два)
при вставке чего-либо с использованием charCount 17 он вставляет это в правильное место (т. е. один два что-то три два что-то)
если бы мы использовали charCount 27, он бы вставил его не в то место.
Почему использование charCount 17 правильно? Я ожидаю, что при использовании 17 при вставке строки она будет вставлена не в то место (т. е.: один, два, что-то, три, два)
Я ожидал, что использование 27 поместит его в правильное место, но это не так.
Кроме того, я бы посоветовал вам дать переменным гораздо лучшие имена. Прямо сейчас у вас есть переменные с именами textarea
, nodes
и node
, каждая из которых имеет разные значения, и ни одна из этих переменных не содержит того, что подразумевает их имя.
Почему вы вычитаете из длины? Это не будет правильным подсчетом для каждого.
добавил правку для ясности. @Рори МакКроссан, у меня вопрос не о прослушивателях событий. Я хотел бы знать, почему длина массива не обновляется, поскольку я знаю, что в некоторых случаях (я не знаю, в каких случаях...) длина массива обновляется при внесении изменений. Почему в некоторых случаях, а не в этом случае? Также спасибо за указатель на имена переменных.
Я не понимаю, как разделение строк по пробелам является подсчетом узлов?
topNode.textContent(" ")
похоже на ошибку. Что ты имел в виду?
спасибо, сейчас отредактирую
«Я ожидаю, что длина nodes
будет равна 4» — в вашем коде нет nodes
(по крайней мере, больше). Вы имели в виду topNodes
или words
?
Проклятие. Отредактировал код, но не текст. Я имел в виду words
. Редактируем текст сейчас
Почему добавление TEXT_NODE в существующий родительский элемент не обновляет количество элементов? Я думал, что при работе с такого рода элементами они «живые», т.е. обновление с внесенными изменениями.
Они делают.
const topNodes = textarea.childNodes;
это не массив, а живой NodeList дочерних элементов этого элемента. Когда вы добавите больше узлов в textArea
, это отразится в коллекции.
Почему
const words = topNode.textContent.split(" ");
не обновляется при добавлении к элементу новых строк.
Потому что это массив, созданный из строки (topNode.textContent
). Ни строка не является «живой», а просто значением, ни метод split()
не знает, как построить «живой массив». Этот код запускается один раз, и если вы не обновите массив самостоятельно, он сохранит состояние, в котором он был инициализирован.
Спасибо. Я отредактировал вопрос (после кода), который отражает (надеюсь, ясно), почему я запутался. Не могли бы вы взглянуть, отредактировать свой ответ, и я приму.
«переменная topNode
является элементом TEXT_NODE; и мы хотим добавить к элементу несколько строк (textNodes)» — нет. Текстовый узел не является элементом , и вы не можете добавлять к нему другие узлы в качестве дочерних. Вы можете изменить только его nodeValue.
И опять же, живыми могут быть только объекты. topNodes
активен, и его .length
действительно изменится, когда вы запустите thisParentNode.parentNode.insertBefore(…)
. Но charCount
— это всего лишь число, и вы сами несете ответственность за обновление переменной.
Почему
const words = topNode.textContent.split(" ");
не обновляется при добавлении новых строк в элемент topNode-s.
Потому что механизма для этого нет и никогда не было. (Я не знаю ни одного языка, который бы это делал. Если бы он когда-либо был реализован, он должен был бы быть либо на новом языке, либо в новом явном синтаксисе только для этой функциональности, поскольку такая вещь фундаментально нарушила бы весь существующий код. .)
topNode
действительно может быть ссылкой на элемент на странице. Но words
наверняка нет. Это просто массив строк, полученный на основе информации об этом элементе. Чтобы получить обновленные данные, вам придется снова запросить этот элемент.
Чтобы упростить ту же операцию, учтите следующее:
let x = 2;
let y = 3;
let z = x + y;
x = 3;
Какую ценность вы ожидаете от z
после этих четырех строк кода и почему?
z
будет равно 5, хотя x
было изменено. Это связано с тем, что значение z
не является живой ссылкой на переменные, используемые для его расчета. Это значение, полученное в результате этого расчета. Обновление значения означает повторное выполнение расчета.
Если вы хотите аппроксимировать эту функциональность, на каком-то уровне для этого нужны функции:
let x = 2;
let y = 3;
let z = () => x + y;
x = 3;
Теперь z
не имеет числового значения, его значение представляет собой функцию, которую можно вызвать для получения нужного значения. Эта функция имеет «живую» ссылку на переменные x
и y
.
Извините, я не совсем понял ваш вопрос?