1

I have the following html:

<!DOCTYPE html>
<html>
<head>
  <script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
  <span>not so important</span>
  <span title="some specific text"><img src="/img/some.gif" /></span>
  <span title="more specific text">important 1</span>
  <span title="more specific text">important 2</span>
  <span title="more specific text">important 3</span>
  <span title="more specific text"><img src="/img/some.gif" /></span>
  <ul>
    <li>example 1</li>
    <li>example 2</li>
    <li>example 3</li>
  <ul>
  <script>
    var ul = $('body').append($('<ul>'));
    $("span[title*='specific text']")
        .contents()
        .filter(function(){ return $(this).nodeType != 1; })
        .each(function(){
            ul.append($('<li>' + $(this).text() + '</li>'));
        })
  </script>
</body>
</html>

I want to retrieve the text within a 'span' whose title attribute contains "specific text", in this case "important 1", "important 2" and "important 3". Then I want to list them below just like the example. However the bullets do not appear, why is that? Any hints would be appreciated!

EDIT: http://jsfiddle.net/XTqjC/3/ this is the code that does what I need it to do. Thanks to Glen and meo

Dan7
  • 1,657
  • 1
  • 19
  • 31

2 Answers2

4

In your example you haven't closed your <ul> properly (missing the slash). Is that a copy+paste error, or in your code as well?

Edit: And you need it is good practice to have a semi-colon after your each() (see comments):

.each(function(){
  ul.append('<li>' + $(this).text() + '</li>');
});

Try the following:

var newUL = $('<ul/>');    //due credit to meo (see below)
$('body').append(newUL);
$("span[title*='specific text']")
    .contents()
    .filter(function(){ return $(this).nodeType != 1; })
    .each(function(){
        $("body ul").append('<li>' + $(this).text() + '</li>');
    });

Of course, the $(this).text() won't allow you to place the <img> in the <li>s. So you'll end up with an empty <li></li> for each. This could be fixed with the following:

.each(function(){
  if ($(this).text().trim() != "") {
    $("body ul").append('<li>' + $(this).text() + '</li>');
  }
});
GlenCrawford
  • 3,359
  • 3
  • 26
  • 34
  • its not very ideal to reselect the the ul in the each. you should put that in a variable. Poor cpu has to execute a lot of code for each loop :/ there is no need for a semi-colon. you only absolutely need them if you are minimizing your code or if you write inline code... – meo Apr 27 '10 at 09:11
  • 1
    @meo: Will do that now...done and credited. As for the semi-colon, true, it's not required, but it's good practice to be as explicit as possible with semi-colons in JS (see here: http://stackoverflow.com/questions/444080/do-you-recommend-using-semicolons-after-every-statement-in-javascript). – GlenCrawford Apr 27 '10 at 09:14
  • 1
    you just told him he NEED semi-colon, thats why i commented it. Its good if he does use them. But its not the problem here. +1 for semi colon link – meo Apr 27 '10 at 09:22
  • 1
    You're right, it's not the problem that caused the solution, but it's good practice in the pursuit of foolproof code that will pass JSLint validation. Edited to reflect. Also, if this code is going to be minified in the future, and there are statements after the each(), the code will likely break due to the missing semi-colon (found that out the hard way). – GlenCrawford Apr 27 '10 at 09:30
  • +1 for semi-colon link and explanation. I always slack off when writing JS code, and actually see this as a good language feature. But the link you provided makes a lot of sense. – Dan7 Apr 27 '10 at 15:00
  • I thought .filter(function(){ return $(this).nodeType != 1; }) already filtered all non-text node? Why are the img nodes still passed onto .each()? And you are right, the
      tag was a mistake, what an oversight. :)
    – Dan7 Apr 27 '10 at 15:06
  • After some fiddling around I found out it should be ".filter(function(){ return this.nodeType == 3; })", since nodeType 3 represents text node. I don't know why jQuery API page's example uses "return this.nodeType != 1" in filter() for the same task (filtering out non-text node). – Dan7 Apr 27 '10 at 15:27
2

I have corrected your code. You had some errors:

http://jsfiddle.net/XTqjC/2/

here is my version:

var ul = $('<ul/>');
$('body').append(ul)
$("span[title*='specific text']")
    .contents() // you actually don't need contents()
    .filter(function(){ return $(this).nodeType != 1; })
    .each(function(){
        ul.append('<li>' + $(this).text() + '</li>');
    })

when you do this: var ul = $('body').append($('<ul>'));

the ul variable contains the body, and your li's where not appended to the UL but the body. and when you use append you dont have to put the $() around

meo
  • 30,872
  • 17
  • 87
  • 123
  • +1 for the clarification of the ul variable and .contents(), didn't know that :) – Dan7 Apr 27 '10 at 14:51
  • Actually ".contents().filter(function(){ return $(this).nodeType == 3; })" should do what it is supposed to do. I got the nodeType check wrong in the original code. – Dan7 Apr 27 '10 at 15:31