Как получить только видимый текст с помощью JQuery (или Javascript)

У меня есть веб-сайт, который преобразует Японский кандзи в ромаджи (римские буквы):

и вывод показывает и скрывает с CSS, что пользователь должен видеть в зависимости от их входных критериев. Например:

<div id="output">
    <span class="roman">watashi</span> 
    <span class="english">I</span>
</div>

Интерфейс позволяет пользователю переключаться между и watashi или I в зависимости от того, что они хотят видеть.

CSS скрывает тот или иной, используя jQuery и кнопку переключения. (механизм скрытия включает в себя простое добавление класса к body и возможность CSS выполнить его).

Проблема заключается в том, что когда пользователи копируют/вставляют текст в Word, он копирует все. Поэтому я решил использовать систему для копирования вставки текста с помощью JavaScript и jQuery, но проблема повторяется:

$('#output').text()

выводит watashi I, даже если I невидим на самой странице а не watashi. Есть ли способ получить только видимый текст?

Ответы

Ответ 1

Используйте : видимый селектор JQuery

В вашем случае я думаю, что вы хотите сделать:

$('#output').children(":visible").text() 

Ответ 2

другие решения не дали мне то, что мне было нужно.

Короткий ответ

мой ответ:

$('#output *:not(:has(*)):visible').text()

plunkr

TL; DR

Проблема с решением marcgg

Вы не должны запрашивать текст всего элемента под некоторым корневым элементом.

почему? - он будет повторять вывод и игнорировать скрытый флаг

рассмотрим простой пример

<div id="output" class="my-root">
    <div class="some-div">
         <span class="first" style="display:none"> hidden text </span>
         <span class="second" > visible text </span>
    </div>
<div>

теперь, если я делаю $('#output').children(":visible").text()

Я получу .some-div и .second.. когда на самом деле .some-div меня не интересует.

когда я запрашиваю text() для этих элементов, .some-div также вернет скрытый текст.

поэтому технически решение marcgg ошибочно ИМХО...

Причина моего ответа

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

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

Итак, мы не увидим что-то вроде этого:

<div id="output" class="my-root">
    <div class="some-div">
         <span class="first" style="display:none"> hidden text </span>
         <span class="second" > visible text </span>
    </div>

    some text here.. 

<div>

Почему это предположение кажется мне разумным? две причины:

  • Потому что сложно поддерживать страницу, которая построена таким образом - и со временем люди с опытом узнают об этом и избегают этого.
  • Легко преобразовать ваш html в такую ​​структуру. просто оберните текст родителей пробелами. Поэтому, даже если это предположение не существует прямо сейчас, его легко добраться.

В этом предположении вы хотите запросить все элементы листа (элементы без детей), отфильтровать видимые и запросить их текст.

$('#output *:not(:has(*)):visible').text()

Это должно привести к правильному результату.

Должен ли текст за пределами элемента листа?

комментарии предполагают, что иногда у вас есть текст вне листового элемента

<div> This is some <strong style="display:none"> text </strong>  </div>

Как вы можете видеть, у вас есть <strong> как лист, и обычно есть текст вне него, как в этом примере.

Вы можете обойти это с обходным решением, которое я предлагаю выше.. но что, если вы не можете?

Вы можете клонировать дом, а затем удалять все скрытые элементы. Проблема здесь в том, что для того, чтобы селектора :visible или :hidden работали, я должен иметь элемент dom в документе (что означает, что он фактически видим для пользователя). Итак, этот метод имеет некоторые побочные эффекты, поэтому будьте осторожны.

Вот пример

для этого html

 <div id="output" class="my-root">
     <span>
         some text <strong style="display:none">here.. </strong>
     </span>
</div>

Этот javascript работает

$(function(){
     var outputClone = $('#output').clone();
    $('#output :hidden').remove(); 
    console.log($('#output').text()); // only visible text
    $('#output').replaceWith(outputClone);
    console.log($('#output').text()); // show original state achieved. 
})

см. plunker здесь

как упоминалось - побочные эффекты могут появляться как мгновенное мерцание или некоторая инициализация script, которые должны запускаться. Некоторым можно избежать с некоторым оригинальным мышлением (div с размером 1px/1px, чтобы содержать клон вместе с исходным контентом?) в зависимости от вашего сценария.

Ответ 3

У Гая есть правильный ответ.

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

$('*:not(:has(*)):visible', this).text()

Ответ 4

var lookup = function(element, text) {
    //DFS Recursive way of finding text on each level
    //Visible only works on elements that take up space(i.e. not fixed position elements)
    var results = element.children(':visible');

    //Look at the text at each level with the children removed
    var newText = '';
    results.each(function(index, value) {
        newText += $(value).clone()
            .children()
            .remove()
            .end()
            .text();
    });

    var moreResultText = '';
    results.each(function(index, value) {
        moreResultText += lookup($(value), text);
    })

    if (results.length > 0) {
        return text + newText + moreResultText;
    } else {
        return text;
    }
};

lookup($('#output'), ''));

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

Будьте осторожны, это не сохраняет никакого смысла форматирования, и расстояние между элементами может быть неточным. Кроме того, он, вероятно, неправильно упорядочивает возвращенный текст, в этих аспектах его использование будет ограничено. Еще одно соображение - реальное определение видимости немного сложно гвоздь вниз, но в этом примере я принимаю, что ": visible" работает для большинства распространенных случаев.

Я использую его, чтобы проверить, содержит ли страница видимый текст (просто запустите его на элементе body), но он, вероятно, тоже будет работать для этого примера.

Ответ 5

Вместо того, чтобы скрывать диапазон, удалите элемент span и сохраните ссылку на него. Когда пользователь нажимает на кнопку переключения, удалите другую и вставьте ту, на которую вы указали ссылку. Пользователь больше не сможет выбирать то, что больше не находится в DOM.

Ответ 6

Попробуйте это в современных браузерах (здесь "элемент" - объект DOM без JQuery):

function getVisibleText(element) {
    window.getSelection().removeAllRanges();

    var range = document.createRange();
    range.selectNode(element);
    window.getSelection().addRange(range);

    var visibleText = window.getSelection().toString().trim();
    window.getSelection().removeAllRanges();

    return visibleText;
}

то

getVisibleText(document.getElementById('output'));