How do I get just the visible text with JQuery (or Javascript)

Use the :visible selector of jQuery

In your case I think you want to do:

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

the other solutions did not give me what I needed.

Short Answer

my answer is :

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

plunkr

TL;DR

The problem with marcgg's solution

You should not ask the text of all element under some root element..

why? - it will repeat output and ignore hidden flag

lets look at a simple example

<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>

now if I do $('#output').children(":visible").text()

I will get .some-div and .second.. when in fact .some-div is of no concern to me..

when I ask for text() on those elements, .some-div will return the hidden text as well..

so technically marcgg's solution is wrong IMHO...

The reason for my answer

Now, in order to properly answer the question, we have to make an assumption. One that, for me, seems reasonable enough.

The assumption is that text only appears in leaf elements..

So we won't see something like this:

<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>

Why does this assumption seem reasonable to me? two reasons:

  • Because it is hard to maintain a page that is constructed this way - and with time people with experience learn that and avoid it.
  • It is easy to convert your html to such a structure. just wrap parents' text with spans. So even if this assumption does not exist right now, it is easy to get there.

With that assumption, what you want to do is request all leaf elements (elements without children) , filter out the visible, and ask for their text..

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

This should generate the correct result.

Gotta have text outside leaf element?

the comments suggest sometimes you just got to have text outside leaf element

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

As you can see, you have <strong> as a leaf and it is common to have text outside it like in this example.

You could go around it with the workaround I suggest above.. but what if you can't?

You can clone the dom and then remove all hidden elements. The problem here is that in order for :visible selector or :hidden selectors to work, I must have the dom element on the document (which means actually visible to the user). And so, this method comes with some side effects, so be careful.

Here is an example

for this html

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

This javascript works

$(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. 
})

see plunker here

as mentioned - side effects may appear like a momentary flicker, or some initialization script that should run.. some may be avoided with some original thinking (div with size 1px/1px to contain the clone alongside original content?) depending on your scenario.


Try this in modern browsers (here 'element' is a non-JQuery DOM object):

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

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

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

    return visibleText;
}

then:

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

Guy has the correct answer.

However, I was dealing with a "this" object, so to get his answer to work you need to use the following syntax...

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