开发者

Need jQuery text() function to ignore hidden elements

I have a div set up something like this:

<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>

EDIT: To clarify, this is the simplest example. The div could have any arbitrary number of n deep nested children.

$('#test').getText() returns 'Hello Goodbye'. Here's a one liner to test in Firebug: jQuery('<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>').text()

This seems to be because what jQuery uses internally, textContent (for non IE), returns hidden elements as part of the text. Hrmph.

Is there a way to return the text content ignoring display:none'd elements? Basically I am trying to mimic the text you would get from highlighting the div with your mouse and copying to system clipboard. That ignores hidden text.

Interestingly, if you create a selection range and get the text from it, that also returns text inside display:none elements.

var range = document.body.createTextRange();
range.moveToElementText($('#test')[0]);
range.select();

console.log(range.toString()); // Also logs Hello Goodbye!

So creating a document selection range doesn't appear to do the same thing as highlighting with the mouse in terms of display:none elements. How do I get around this dirty pickle conundrum?

Edit: using .filter(':visible').text has been suggested, but it won't work for this scenario. I need the returned text to be EXACTLY what would come from a selection with the mouse. So for example:

$('<div>test1 <p>test2</p>\r\n <b>test3</b> <span style="display:none">none</span></div>').appendTo(document.body).children().filter(':visible').text()

returns

"test2test3"

When the output I actually want is

test1 te开发者_Go百科st2
 test3

linebreaks, whitespace and all, which come from the \r\n


Filter the elements using .filter(":visible").

Or use this:

$("#test :visible").text();

But the jQuery documentation advises us to use .filter() instead:

Because :visible is a jQuery extension and not part of the CSS specification, queries using :visible cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :visible to select elements, first select the elements using a pure CSS selector, then use .filter(":visible").


Use :visible in your selector as such:

$("#test > p:visible").text()

A Function example:

-- Edit:

http://jsfiddle.net/8H5ka/ ( Works on Chrome it displays "Hello" in Result )

If the above doesn't work:

http://jsfiddle.net/userdude/8H5ka/1/


If space isn't a major concern you could copy the markup, remove the hidden elements, and output that text.

var x = $('#test').clone();
x.filter(':not(:visible)').remove();
return x.text();


I had this problem and found this question, and it seems the actual solution is based on the provided answers but not actually written out. So here's a complete solution that worked for my situation, which is the same as the OP with the additional provision that elements may be invisible due to external styles based on DOM position. Example:

<style>.invisible-children span { display: none; }</style>
<div class="invisible-children">
  <div id="test">Hello <span>Goodbye</span></div>
</div>

The solution is to:

  1. Make a clone of the entire object.
  2. Remove invisible objects in place; if we take #test out of the DOM before we remove invisible objects, jQuery might not know they're invisible because they will no longer match the CSS rules.
  3. Get the text of the object.
  4. Replace the original object with the clone we made.

The code:

var $test = $('#test');
// 1:
var $testclone = $test.clone();
// 2: We assume that $test is :visible and only remove children that are not.
$test.find('*').not(':visible').remove();
// 3:
var text = $test.text();
// 4:
$test.replaceWith($testclone);
// Now return the text...
return text;
// ...or if you're going to keep going and using the $test variable, make sure
// to replace it so whatever you do with it affects the object now in DOM and
// not the original from which we got the text after removing stuff.
$test = $testclone;
$test.css('background', 'grey'); // For example.


Here is how I did it with MooTools:

$extend(Selectors.Pseudo, {
    invisible: function() {
        if(this.getStyle('visibility') == 'hidden' || this.getStyle('display') == 'none') {
            return this;
        }
    }
});

Element.implement({
    getTextLikeTheBrowserWould = function() {
        var temp = this.clone();
        temp.getElements(':invisible').destroy();
        return temp.get('text').replace(/ |&amp;/g, ' ');
    }
})


I search for that and found this question but without solution. Solution for me is just get out of jquery to use DOM:

var $test = $('#test').get(0).innerText

or if more than on element in array of selector, you need a for loop and a merge but I guess that most of time it is the first version that you need.

var $test = $('#test').get().map(a => a.innerText).join(' ');
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜