#javascript #string #search #dom #text
#javascript #строка #Поиск #дом #текст #dom
Вопрос:
Я пытаюсь выяснить, как в необработанном javascript (без jQuery и т.д.) Найти элемент с определенным текстом и изменить этот текст.
Мое первое воплощение решения… этого недостаточно. То, что я сделал, было в основном:
var x = document.body.innerHTML;
x.replace(/regular-expression/,"text");
document.body.innerHTML = x;
Наивно я думал, что преуспел с блеском, тем более что это было так просто. Итак, затем я добавил изображение к своему примеру и подумал, что могу проверять каждые 5 секунд (потому что эта строка может динамически входить в DOM)… и изображение мерцало каждые 5 секунд.
Упс.
Итак, должен быть правильный способ сделать это. Способ, который специально выделяет определенный элемент DOM и обновляет текстовую часть этого элемента DOM.
Теперь всегда есть подход «рекурсивный поиск по дочерним элементам, пока не найдете самый глубокий дочерний элемент со строкой», которого я хочу избежать. И даже тогда я скептически отношусь к тому, что «изменение innerHTML на что-то другое» является правильным способом обновления элемента DOM.
Итак, каков правильный способ поиска строки в DOM? И каков правильный способ обновить текст элемента DOM?
Комментарии:
1. Интересное решение с использованием
document.evaluate
и XPath, но это, к сожалению, не работает в IE, что, я полагаю, делает его спорным: jsfiddle.net/GRmF5/6 . Существует служебная функция, которая также используетtextContent
/innerText
вместоinnerHTML
.
Ответ №1:
Теперь всегда есть подход «рекурсивный поиск по дочерним элементам, пока не найдете самый глубокий дочерний элемент со строкой», которого я хочу избежать.
Я хочу выполнить поиск элемента в неупорядоченном случайном списке. Теперь есть подход «просматривайте все элементы, пока не найдете то, что ищете», которого я хочу избежать.
Старомодный магнитофон, записывайте, слушайте, медитируйте.
Кстати, смотрите: Найдите и замените текст с помощью JavaScript на github
Джеймса Падолси (также есть статьи объясняющие это)
Комментарии:
1. В блоге упоминается обновленная ссылка на некоторые новые идеи: j11y.io/javascript/replacing-text-in-the-dom-solved Сопутствующий js работает как шарм.
2. Ссылка мертва, доступна через: archive.org web.archive.org/web/20161215111336/http://james.padolsey.com /…
3. и web.archive.org/web/20161120091844/http://james.padolsey.com/…
4. обновил ссылки, спасибо
5. @gblazex Обычно считается лучшим, более вежливым ответом, если вы предоставляете описание (или хотя бы краткое изложение) того, что описано на странице, а не просто ссылку. (Позволяет пользователям не переходить по ссылкам, а также неработающим ссылкам, как было замечено выше.)
Ответ №2:
Редактировать: Изменен querySelectorAll на getElementsByTagName по предложению Робга.
Вы можете использовать функцию getElementsByTagName, чтобы захватить все теги на странице. Оттуда вы можете проверить их дочерние элементы и посмотреть, есть ли у них какие-либо текстовые узлы в качестве дочерних. Если они это сделают, вы бы посмотрели на их текст и посмотрели, соответствует ли он тому, что вам нужно. Вот пример, который распечатает текст каждого текстового узла в вашем документе с помощью объекта console:
var elms = document.getElementsByTagName("*"),
len = elms.length;
for(var ii = 0; ii < len; ii ) {
var myChildred = elms[ii].childNodes;
len2 = myChildred.length;
for (var jj = 0; jj < len2; jj ) {
if(myChildred[jj].nodeType === 3) {
console.log(myChildred[jj].nodeValue);
// example on update a text node's value
myChildred[jj].nodeValue = myChildred[jj].nodeValue.replace(/test/,"123");
}
}
}
Чтобы обновить текст элемента DOM, просто обновите свойство nodeValue текстового узла.
Комментарии:
1. Нет необходимости использовать qSA, который доступен только в самых последних браузерах.
getElementsByTagName('*')
будет делать то же самое (более или менее, он возвращает текущий список, а не статический, но одни и те же элементы находятся в обоих списках в одинаковом порядке) и поддерживается всеми используемыми браузерами.2. Хорошая уловка, Робг, ты прав. В данном случае это лучший выбор, чем querySelectorAll.
Ответ №3:
Не используйте innerHTML с регулярным выражением, это почти наверняка приведет к сбою для нетривиального содержимого. Кроме того, все еще существуют различия в том, как браузеры генерируют его из live DOM. Замена innerHTML также приведет к удалению любых прослушивателей событий, добавленных в качестве свойств элемента (т. Е. подобных element.onclick = fn
).
Лучше всего, если вы можете поместить строку, заключенную в элемент с атрибутом или свойством, по которому вы можете выполнять поиск (id, class и т.д.), Но в противном случае поиск по текстовым узлам является наилучшим подходом.
Редактировать
Попытка использовать функцию выбора текста общего назначения для HTML-документа может привести к очень сложному алгоритму, поскольку строка может быть частью сложной структуры, например:
<h1>Some <span class="foo"><em>s</em>pecial</span> heading</h1>
Поиск строки «специальный заголовок» является сложной задачей, поскольку она разделена на 2 элемента. Обернуть его другим элементом (скажем, для выделения) также нетривиально, поскольку результирующая структура DOM должна быть допустимой. Например, текст, соответствующий «some special» в приведенном выше примере, может быть заключен в span, но не в div.
Любая такая функция должна сопровождаться документацией с указанием ее ограничений и наиболее подходящего использования.
Комментарии:
1. Не может зависеть от того, находится ли он во вложенном элементе. Мне нужен очень универсальный интерфейс, потому что я хочу, чтобы люди могли вставлять этот код на любую страницу. Это не для меня, это для наших конечных клиентов.
2. 1 за потерянное упоминание прослушивателей событий. Наиболее распространенный игнорируемый побочный эффект innerHTML.
3. 1 Итак, обработчики событий, прикрепленные с помощью
addEventListener()
orattachEvent()
, все еще будут работать при сериализации HTML и последующем его сбросе? Никогда не знал этого… Я всегда думал, что все они сломаются.
Ответ №4:
Забудьте о регулярных выражениях.
Выполните итерацию по каждому текстовому узлу (и выполнение этого рекурсивно будет наиболее элегантным) и измените текстовые узлы, если текст найден. Если вы просто ищете строку, вы можете использовать indexOf()
.
Комментарии:
1. Как мне выполнить итерацию по каждому текстовому узлу? Я никогда раньше этого не делал.
2. @rk1s Эта статья , кажется, подходит к этому.
3. Я думаю, что в этой статье все получилось. спасибо 🙂 просто нужно попробовать это сейчас
4. Поиск не обязательно должен быть рекурсивным, он может выполняться итеративно по списку узлов, возвращаемому getElementsByTagName .
5. @RobG Конечно. Но я никогда не говорил, что это должно быть.
Ответ №5:
x.replace(/regular-expression/,"text");
вернет значение, поэтому
var y = x.replace(/regular-expression/,"text");
теперь вы можете присвоить новое значение.
document.body.innerHTML = y;
Но вы хотите подумать об этом, вы же не хотите получать все тело только для изменения одного небольшого фрагмента кода, почему бы не получить содержимое div или любого элемента и так далее
пример:
<p id='paragraph'>
... some text here ...
</p>
теперь вы можете использовать javascript
var para = document.getElementById('paragraph').innerHTML;
var newPara = para.replace(/regex/,'new content');
para.innerHTML = newPara;
Это должен быть самый простой способ.
Комментарии:
1. Просто, пока они не захотят искать что-либо, что будет искажать сериализованный HTML, очень реальная возможность.
2. да, не все так просто, я думаю, когда вы ищете сложные шаблоны
3. Я не могу зависеть от того, что это где-то конкретно. Мне нужен универсальный и динамичный, потому что он должен работать на любой веб-странице.