12

I have an example element like this:

<div id="element">
  Blah blah blah.
  <div>Header</div>
  ...
</div>

it can also look like this:

<div id="element">
  <span>Blah blah blah.</span>
  <h4>Header</h4>
  ...
</div>

I want to get the first line (defining line loosely) (Blah blah blah.) of text inside the element. It might be wrapped inside a child element or just be naked textnode. How do I do that? Thanks.

Harry
  • 52,711
  • 71
  • 177
  • 261

5 Answers5

16

Use the contents()[docs] method to get all children, including text nodes, filter out any empty (or whitespace only) nodes, then grab the first of the set.

var first_line = $("#element")
                       .contents()
                       .filter(function() { 
                           return !!$.trim( this.innerHTML || this.data ); 
                       })
                       .first();

text node: http://jsfiddle.net/Yftnh/

element: http://jsfiddle.net/Yftnh/1/

user113716
  • 318,772
  • 63
  • 451
  • 440
  • thanks, though for me this returns an object rather than text, should I just first_line[0]? – Harry Sep 11 '11 at 20:50
  • Yes, this gives you a jQuery object with either the element or the text node. If you want the text content, use `first_line.text()`. If you want to trim away leading and trailing whitespace, do `$.trim( first_line.text() )`. – user113716 Sep 11 '11 at 20:52
  • 1
    Hi can you explain why you do return !!? What is !!? – Harry Sep 11 '11 at 20:53
  • 1
    It converts to the value's boolean equivalent. After trimming, if `.innerHTML` or `.data` returns an empty string, `!!` will convert it to boolean `false`, because an empty string is considered a "falsey" value. Any non-empty string will result in `true`. http://stackoverflow.com/questions/4686583/can-someone-explain-this-double-negative-trick/4686608#4686608 – user113716 Sep 11 '11 at 20:54
  • 2
    +1 from me, but the performance-wary might want to consider using `$.each()` rather than `filter()`. It's a bit late in the day for me, but an example of this is http://jsfiddle.net/AndyE/Yftnh/3/ (ignore revision 2, I'm half asleep here!) – Andy E Sep 11 '11 at 20:56
  • @Andy E do you have a link or something that explains the significance of filter vs each for performance? – Harry Sep 11 '11 at 20:57
  • 2
    @Andy E: Nice one. Great use of the comma operator too. @Harry: It just lets you stop the loop once you've found a non-empty node. With `.filter()` you have to let the loop run for each element that `.contents()` returns. – user113716 Sep 11 '11 at 20:58
  • @Harry: pretty much what @patrick_dw said. If the contents of the element turns out to be 200 nodes, the `filter()` function runs on each one of those nodes. `each()` can be broken so that it stops running after it finds what you're looking for. – Andy E Sep 11 '11 at 21:02
  • 1
    @Andy E: For fun, here's one sans comma `return !$.trim((first_line = this).innerHTML||this.data);` :) – user113716 Sep 11 '11 at 21:03
3

Here is idea for you. Of course if there is h4 element before line you want to get:

var content = $('#element').html();
var arr = content.split('<h4>');
console.log(arr[0]);
simoncereska
  • 3,035
  • 17
  • 24
2
var myElement = $("#element");
while(myElement.children().length > 0)
{
   myElement = myElement.children().first();
}
var firstText = myElement.text();

Assuming that the text is correctly wrapped inside an element, of course. You might check whether there's text before the first element:

/\s*</.test(myElement.html())
Maximilian Hils
  • 6,309
  • 3
  • 27
  • 46
  • Doesn't work for the first case I believe, that'll match the Header rather than the blah blah blah (maybe should've picked better example text lol) – Harry Sep 11 '11 at 20:36
  • If you get
    mytext
    it gets pretty difficult. You need something recursively traveling through the DOM i guess. How bulletproof does it have to be?
    – Maximilian Hils Sep 11 '11 at 20:42
1

the counter starts at 1

myElement.text().split('\n')[1]
Mo D Genesis
  • 5,187
  • 1
  • 21
  • 32
1

first get the content of the element

var content = $('#element').html();

then split the text into an array using line break

tmp = content.split("\n");

the first part of the array is the first line of the element

var firstLine = tmp[0];
clem
  • 3,345
  • 1
  • 22
  • 33
  • 1
    I first thought, what linebreak, but I see in Fx at least there is. It is however dangerous to rely on. Here is the first line: `alert($("#element").text().split(/\n/)[1])` the [0]th is an empty string... http://jsfiddle.net/mplungjan/QquSp/ – mplungjan Sep 11 '11 at 20:33