Treehouse: Grow your CSS skills. Land your dream job.

Last updated on:

Underline Individual Words

There is no CSS for applying an underline (text-decoration: underline;) only to individual words in a multiple-word element. The best way would be to wrap each word in a span (not the spaces, just the words) in spans and apply underline to those spans. Here's jQuery to do that to h1 elements.

$('h1').each(function() {

	var words = $(this).text().split(' ');

	$(this).empty().html(function() {

		for (i = 0; i < words.length; i++) {
			if (i == 0) {
				$(this).append('<span>' + words[i] + '</span>');
			} else {
				$(this).append(' <span>' + words[i] + '</span>');
			}
		}
	
	});

});

Then you could do:

h1 span {
  text-decoration: underline;
}

Similar and slightly more robust solution: Lettering.js

Reference URL

Comments

  1. Jan-Marten de Boer
    Permalink to comment#

    For the raw JS version:

    var h1s = document.getElementsByTagName('h1');
    for(var i=0; i < h1s.length; i++) {
        var t = h1s[i];
        t.innerHTML = '<span>' + t.innerHTML . split(' ') . join('</span><span>') +  '</span>'; 
    }

    I believe I reduced the footprint a little too. Although your version is a lot more abstract and uses better maintainable code.

    • Jan-Marten de Boer
      Permalink to comment#

      Whoah, sorry :P There should be a space between </span> and <span>

      join('</span> <span>')
  2. Jan-Marten de Boer
    Permalink to comment#

    Just a note: If you do a for loop, you should declare the i variable with the var keyword. See here why.

    Furthermore, if this for-loop sees the length is 0, it will never reach the inside of the for loop and breaks off before initiating the code inside. Therefore, your if-else-statement is completely ignored, since only the else case can be true. If will never be reached, because the for loop breaks off before then.

  3. Adam
    Permalink to comment#

    The code in the reference is correct, but that code on this page is missing the span tags within append().

  4. TeMc
    Permalink to comment#

    Although the raw JS version isn’t too bad either. There’s some performance to be gained in the jQuery version as wel. Why call $.fn.each, $.fn.empty, $.fn.html and then a dozen times $.fn.append ?

    
    function htmlEsc(txt){
      return txt.replace(/&/g, "&").replace(//g, ">");
    }
    $('h1').each(function() {
    
            var $el = $(this),
                words = $el.text().split(' '),
                newHtml = '',
                i = 0, 
                len = words.length;
    
            for ( ; i < len; i++) {
                    if (i < 0) {
                           newHtml += ' '; // space
                    }
                    newHtml += '' + htmlEsc( words[i] ) + '';
            }
    
            $(this).html( newHtml );
    
    });
    

    * the empty() is simply redundant
    * the calls to append() don’t make sense inside html. Either build outside and call $(this).append() a few times, or build it inside and call $(this).html( stuff ); The latter is recommended.
    * You’re using text() to retrieve the value but using html()/append() to put it back. This is very dangerous when working with user input and can also destroy your layout if there is anything like HTML in your titles. You must escape html here when building the strings (or use $.fn.text)
    * Don’t forget ‘var’ instead of i in the for loop. Or move it to the list of vars outside the for-loop, that way you save a few bits in not starting two var statements (those are all one var statement, not how they are separated by a comma.

  5. TeMc
    Permalink to comment#

    Although the raw JS version isn’t too bad either. There’s some performance to be gained in the jQuery version as wel. Why call $.fn.each, $.fn.empty, $.fn.html and then a dozen times $.fn.append ?

    
    function htmlEsc(txt){
      return txt.replace(/&/g, "&").replace(//g, ">");
    }
    $('h1').each(function() {
    
            var $el = $(this), words = $el.text().split(' '), newHtml = '',  i = 0,  len = words.length;
    
            for ( ; i < len; i++) {
                    if (i < 0) { newHtml += ' ';  } // add space between words
                    newHtml += '' + htmlEsc( words[i] ) + '';
            }
    
            $(this).html( newHtml );
    });
    

    * the empty() is simply redundant
    * the calls to append() don’t make sense inside html. Either build outside and call $(this).append() a few times, or build it inside and call $(this).html( stuff ); The latter is recommended.
    * You’re using text() to retrieve the value but using html()/append() to put it back. This is very dangerous when working with user input and can also destroy your layout if there is anything like HTML in your titles. You must escape html here when building the strings (or use $.fn.text)
    * Don’t forget ‘var’ instead of i in the for loop. Or move it to the list of vars outside the for-loop, that way you save a few bits in not starting two var statements (those are all one var statement, not how they are separated by a comma.

  6. Ecatherina
    Permalink to comment#

    Chris, tell me please how to put in the script, not only h1, but more and h2.
    Thank you in advance , Ecatherina.

  7. Deji
    Permalink to comment#

    thanks

  8. Deji
    Permalink to comment#

    i need some css code pls oga

  9. ilham
    Permalink to comment#

    Your tutor is very nice guys…
    like it’s..

  10. Buy Cheap Diablo 3 Identify
    Permalink to comment#

    Nice game! The thing that really irks us a lot is having if an individual online connection to play in solo mode. I mean seriously wtf?

  11. Yukulélé
    Permalink to comment#

    my version accept non-final nodes !

    gist :

    https://gist.github.com/yukulele/8668962

    pen :

Leave a Comment

Posting Code

We highly encourage you to post problematic HTML/CSS/JavaScript over on CodePen and include the link in your post. It's much easier to see, understand, and help with when you do that.

Markdown is supported, so you can write inline code like `<div>this</div>` or multiline blocks of code in in triple backtick fences like this:

```
<script>
  function example() {
    element.innerHTML = "<div>code</div>";
  }
</script>
```