Заменить ↵ (\ n) в не отображаемом (не отображаемом) тексте элемента - PullRequest
0 голосов
/ 24 сентября 2018

Я пишу парсер, который получает данные из скрытых фреймов.

В тексте мне нужно заменить \n (↵) символов на (пробел).Я использую это для этой задачи - text.replace(/\n/gi, " ").Тем не менее, это работает только для видимых элементов (то есть не имеют display: none).Если элемент не виден (display: none), новые строки просто исчезают и не получают никакой замены.

HTML Пример:

<div data-custom="languages">
    <div>
        <div>
            <h2>
                <span>Just a text that will be removed</span>
            </h2>
            <p>A - b</p>
            <p>c - d</p>
        </div>
    </div>
</div>

Пример JS:

visibleIframe.style.display = "block";
invisibleIframe.style.display = "none";

const visibleDivWithNestedDivs = visibleIframe.querySelector(`[data-custom="languages"]`);
const invisibleDivWithNestedDivs = invisibleIframe.querySelector(`[data-custom="languages"]`);

const visibleText = visibleDivWithNestedDivs.innerText; // "A - b↵c - d"
const invisibleText = invisibleDivWithNestedDivs.innerText; // "A - b↵c - d"

console.log(visibleText.replace(/\n/gi, " ")); // "A - b c - d" (expected result)
console.log(invisibleText.replace(/\n/gi, " ")); // "A - bc - d" (unexpected result, no space between "b" and "c")

Что я пробовал:

.replace(/\n/gi, " ")
.replace(/\r\n/gi, " ")
.replace(/↵/gi, " ")
.replace(/↵↵/gi, " ") // in some cases there was two of this.
.split("↵").join(" ") 
.split("\n").join(" ")
white-space: pre
white-space: pre-wrap

Вы тестировали?

Я на 99% уверен, что это из-за display: none.Я проверил это, и другое отображение iframes дает мне другой результат.

TextContent

Мне не нужно textContent, потому что это возвращает текст без символов \n,Я использую innerText.

Вопросы:

  1. Может ли неожиданный результат быть не из-за этого display: none?
  2. Как мне сделать, чтобы достичь ожидаемого результата?

Ответы [ 2 ]

0 голосов
/ 24 сентября 2018

Итак, согласно удивительному ответу Жака Гупиля , я создал свой собственный обходной путь.Он использует innerHTML.

Алгоритм:

  1. Получить innerHTML элемента.
  2. Удалить объекты.
  3. Удалить HTML-содержимое (теги и т. Д.).
  4. Заменить несколько пробелов одним пробелом.
  5. Заменить пробел между словами разделителем.

Предупреждения:

  • Это просто обходной путь.
  • Это довольно медленно и не подходит для обычногоИспользование !
  • Это парсинг HTML с регулярными выражениями. Это действительно опасно и может разбить все на части. Убедитесь, что регулярное выражение соответствует вашей HTML-структуре.

Код:

/**
 * Returns a text value of the element (and it's childs).
 *
 * @param dcmnt {Document}
 * The `document` where an element will be searched for.
 *
 * @param selector {string}
 * A selector by which will be search.
 *
 * @param separator {string}
 * A separator between the text of an different elements.
 * Defaults to `" "` (one space).
 *
 * @returns {string}
 * If the element was found, then it's text value, else an empty string.
 *
 * Warning!
 * 
 * This method is pretty slow, because it parse HTML slice,
 * not just gets a text value. It is necessary because of elements
 * that was not rendered (i.e. that have `display: none`).
 * `innerText` and `textContent` will return inappropriate result
 * for this kind elements.
 * For more see:
 *
 * @see /11361470/zamenit-n-v-ne-otobrazhaemom-ne-otobrazhaemom-tekste-elementa
 */
function getTextValue(dcmnt, selector, separator) {
    separator = separator || " ";
    const element = dcmnt.querySelector(selector);

    if (!element) {
        return "";
    }

    /**
     * @see https://stackoverflow.com/questions/7394748/whats-the-right-way-to-decode-a-string-that-has-special-html-entities-in-it#7394787
     */
    const _decodeEntities = (html) => {
        const textArea = document.createElement("textarea");
        textArea.innerHTML = html;

        return textArea.value;
    };

    let innerHTML = element.innerHTML;

    // remove entities from HTML, but keep tags and other stuff.
    innerHTML = _decodeEntities(innerHTML);

    // replace HTML stuff with a space.
    // @see https://stackoverflow.com/questions/6743912/get-the-pure-text-without-html-element-by-javascript#answer-6744068
    innerHTML = innerHTML.replace(/<[^>]*>/g, " ");

    // replace multiple spaces with a single space.
    innerHTML = innerHTML.replace(/\s+/g, " ");

    // remove space from beginning and ending.
    innerHTML = innerHTML.trim();

    // for now there only one space between words.
    // so, we replace a space with the separator.
    innerHTML = innerHTML.replace(/ /g, separator);

    return innerHTML;
}

Суть .

0 голосов
/ 24 сентября 2018

Во-первых, давайте разберемся с некоторыми недоразумениями на основе предоставленных вами примеров.

- это символ Unicode, описанный как СТРЕЛКА ВНИЗ С УГОЛОМ ВЛЕВО.Конечно, он хорошо отображает разрыв строки или клавишу Return / Enter, но это не имеет никакого значения в коде.Если вы используете этот символ в регулярном выражении, регулярное выражение будет пытаться найти соответствие для текста, который содержит символ стрелки.

В большинстве языков программирования \n в строке представляет разрыв строки, и вы неНе нужно беспокоиться о том, как это представлено под капотом, будь то с CR, LF или обоими.Поэтому я бы не стал использовать \r в JavaScript.

.replace(/\n/gi, " ") - это совершенно допустимый вариант, в зависимости от того, что вы хотите сделать.Однако вы можете заменить любую последовательность пробелов, которая включает переводы строки.В этом случае я бы использовал это вместо: .replace(/\s+/, " ").Специальный код \s в RegExp соответствует любому виду пробелов, включая разрывы строк.Добавление + позволяет ему соответствовать любой последовательности пробелов.Использование этого гарантирует, что строка типа "a \n \n b" будет преобразована в "a b".

Теперь, когда проблемы с регулярными выражениями решены, давайте рассмотрим innerText.В соответствии с HTML Living Standard , который я нашел, просмотрев статью MDN для innerText , свойство innerText является приблизительным значением, которое пользователь получит при копировании текстаиз этого элемента.Он определяется следующим образом:

Если этот элемент не отображается или пользовательский агент не является пользовательским агентом, не относящимся к CSS, то вернуть то же значение, что и атрибут IDL textContent для этого элемента.Примечание. Этот шаг может привести к неожиданным результатам: например, когда к атрибуту innerText обращаются к элементу, который не отображается, его текстовое содержимое возвращается, но при доступе к элементу, который отображается, все его дочерние элементы, которые не отображаются, имеютих текстовое содержимое игнорируется.

Это ответ, почему может быть разница между видимыми и скрытыми элементами.Что касается количества разрывов строк, алгоритм, который определяет, сколько разрывов строк в строке определяется рекурсивно на стандартной странице , и это довольно запутанно, поэтому я бы посоветовал не основывать вашу логикуна поведение этой функции.innerText является приблизительным.

Я предлагаю взглянуть на textContent , который не подвержен влиянию CSS.

Итак, чтобы закончить это долгообъяснение:

  1. Да, display: none влияет innerText
  2. Я мог бы использовать foo.textContent.replace(/\s+/g, " ") в зависимости от вашей цели.
...