Поиск элемента DOM с определенным текстом и его изменение

#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() or attachEvent() , все еще будут работать при сериализации 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. Я не могу зависеть от того, что это где-то конкретно. Мне нужен универсальный и динамичный, потому что он должен работать на любой веб-странице.