19

I want to attach a Font Awesome icon to the last word in a text string:

foo bar●

But if a line wrap is required, I want the icon to attach to the last word and have them wrap together:

foo
bar●

My problem is very similar to: Prevent :after element from wrapping to next line

However, the solution assumes I have access to the last word. In my case, the text will be generated dynamically. Is there a css/html-only way to ensure the icon wraps with the last word?

Here's a jsfiddle.

Community
  • 1
  • 1
Chad Kruse
  • 623
  • 1
  • 8
  • 22
  • I can't find a way of doing this in CSS alone as the last word needs linking with the icon. However, it's remarkably easy if you're willing to use a small JS function! – nickhar Apr 28 '13 at 13:25
  • @nickhar i have the same problem but i cannot use width as the OP has used for the container. if you could help me with this small JS function, it would be helpful. – Lucian Dec 07 '16 at 13:28
  • Here's my answer to a very similar question that perhaps is overly simple, but it works: https://stackoverflow.com/a/57815452/491553 – Ryan Shillington Sep 06 '19 at 03:48

3 Answers3

14

I just wanted to point out why the change that @Unmocked provided actually works, and it has to do with the difference between display: inline and display: inline-block.

To explain, here's a little background on the difference between block display and inline display.

Block Display

block elements, in general, are elements which cause layout. They receive their own bounding box, and have the ability to push other elements around to some degree in order to fit into the space available within the browser's rendering area. Examples of block items include: h1, p, ul, and table. Note that each of these items, by default, starts a new line and also ends its own line when closed. In other words - they fit into a block of text by creating their own paragraph-level section.

Inline Display

inline elements, in general, are displayed in-line with the text. In other words, they are designed to display with-in the line of text. Examples include i, b, or span. Note that each of these items, by default, continues with the flow of its surrounding text, without forcing a newline before or after itself.

Enter the mid-way case...

Inline-Block Display

inline-block is a hybrid of the above two. In essence, an inline-block element starts wherever its preceding text leaves off, and attempts to fit into the flow of the document inline. However, if it reaches a point where it needs to wrap, it will drop to a new line, as if it were a block element. Subsequent text will then continue immediately following the inline-block element, and continue wrapping as normal. This can lead to some strange situations.

Here is an example, to show what I mean. To see it in action, check out this cssdesk snippet.

enter image description here

In your example, you were setting the :after pseudo-element to be display: inline-block - which meant that when it happened to extend past the right-most boundary of the wrapping display, it acted like the light-blue text in the example - and wrapped, as a block. When you instead change the :after element to display: inline, it causes it to act just like any other text - and without whitespace between it and the preceding letters, the word-wrap acts as you wanted it to.

I hope this helps!

Note: The other thing which changed between your original fiddle and the updated one is the elimination of white-space around the text.

In the first fiddle, your HTML looks like:

<div class="container2">
    foo bar
</div>

While the browser doesn't display the spaces before and after the text, it does contain the spaces, and when the :after element is rendered, it is as if this is happening:

<div class="container2"> foo bar :after</div>

The browser compresses the multiple spaces into single spaces, but still puts a space between the word "bar" and the :after element. Just like a space between any other words, this will cause the wrap to occur after "bar" but before :after at a certain width.

In the second fiddle, you are using:

<div class="container-wide">foo bar</div>
<div class="container-narrow">foo bar</div>

In this case, because there are no spaces trailing the "foo bar" strings, the browser renders the :after element displayed immediately following the end of the content string. It is as if this is happening:

<div class="container-narrow">foo bar:after</div>

No break in inline-rendered text means no break.

For an example demonstrating this, please see this update to your jsFiddle.

Community
  • 1
  • 1
Troy Alford
  • 26,660
  • 10
  • 64
  • 82
  • 3
    This type of answer is the reason I use SO and indeed why it's worth offering a bounty on an unanswered question! – nickhar May 01 '13 at 00:00
  • Ahh - what a great compliment. :) Thank you! – Troy Alford May 01 '13 at 17:14
  • 1
    This is very useful, although to improve clarity, it might be an idea to re-word the first line in the CSSDesk snippet reading "... inline blocks of text ...". As it stands, it reads similarly to the "... inline-block blocks of text ..." (which appears later) and this could cause confusion. Is "blocks of text" a necessary/strict term, or could we call them "chunks" or "sections" of text, for example? – Ollie Bennett Dec 31 '13 at 14:04
8

Interestingly enough, I found you only need to change one line of code for this to work. Testing on your .container2 element in jsFiddle, change this

.container2:after {
    font-family: FontAwesome;
    content: "\f111";
    display: inline-block;
}

to

.container2:after {
    font-family: FontAwesome;
    content: "\f111";
    display: inline;
}

It seems to work with any width I set the container to and will stay connected to what ever foo will be.

Troy Alford
  • 26,660
  • 10
  • 64
  • 82
Unmocked
  • 106
  • 3
  • Simple but effective. Did the trick for me...thanks! Here's a cleaned up [jsfiddle](http://jsfiddle.net/6jh2j/1/) for anyone else that happens upon this. – Chad Kruse Apr 29 '13 at 16:00
  • Did you test this cross-browser? Or just in the browser you use? – nickhar Apr 29 '13 at 22:45
  • @nickhar I haven't done any cross-browser testing (yet) as my immediate use case was for a prototype (on Chrome). I'll be sure to update when I do. – Chad Kruse Apr 30 '13 at 18:19
  • It works in Trident (IE Framework) browsers, I can seem to get my Gecko browsers to work currently. – Unmocked May 01 '13 at 21:53
3

You can use negative margin the width of the icon and css transform. Here a fiddle how to do :

    .container {
        width: 50px;
    }
    .icon-circle {
        background:black;
        height:10px;
        width:10px;
        display: inline-block;   
        margin-left:5px;
    }
    .ANSWER{
        display:block;
        padding-right:15px; /* width of the icon */
    }
    .ANSWER .icon-circle{
        margin-left:-10px;
        transform:translate(15px);
    }
    <h4>What we want</h4>
    <div class="this-is-how-it-should-wrap">
        foo bar<i class="icon-circle"></i>
    </div>

    <div style="margin-top:25px"></div>

    <h4>What happenned</h4>
    <div class="container NORMAL">
        foo bar<i class="icon-circle"></i>
    </div>

    <div style="margin-top:25px"></div>

    <h3>The answer</h3>
    <div class="container ANSWER">
        foo bar<i class="icon-circle"></i>
    </div>

Have a nice day!

Beauceron
  • 624
  • 5
  • 14