103

I have this HTML:

<ul>
    <li class="completed"><a href="#">I want the icon to stay on the same line as this last <strong>word</strong></li>
</ul>

I'm appending an icon using the :after pseudo element:

ul li.completed a:after {
    background:transparent url(img.png) no-repeat; 
    content: '';
    display:inline-block;
    width: 24px;
    height: 16px;
}

The problem: if the available width is too small, the icon wraps to the next line. I would want it to stay on the same line as the last word of the link it's appended to:

enter image description here

Is this doable, without adding 'nowrap' to the entire link (I want the words to wrap, just not the icon).

See jsFiddle here.

ptriek
  • 9,040
  • 5
  • 31
  • 29

11 Answers11

105

JSFiddle - http://jsfiddle.net/Bk38F/65/

Add a negative margin to the pseudo element, to compensate its width:

ul li.completed a:after {
    background:transparent url(img1.png) no-repeat; 
    content: ' ';
    display: inline-block;
    width: 24px;
    height: 16px;
    margin-right: -24px;
}

Now, that is going to make the icon overflow the container, and the line is going to break only when the text meet the end of the line. If you need to maintain the icon inside the original container width, you can add padding to compensate again:

ul li.completed a {
    display: inline-block;
    padding-right: 24px;
}

IMPORTANT UPDATE:

For this method to work, there must be no white space between the text and the closing tag of the element holding the pseudo element. Even though the pseudo element's width is compensated by a negative margin, the white space will make the line break when it meets the edge of the parent element. For example, this works:

<span>text goes here</span>

and this doesn't:

<span>
    text goes here
</span>
budi
  • 6,351
  • 10
  • 55
  • 80
Hugo Silva
  • 6,748
  • 3
  • 25
  • 42
  • This is the more elegant solution in my opinion, since there is no extra markup required. My only complaint is that any spacing you want between the icon and text needs to be added to the image, margin/padding won't work. – brendanmckeown Apr 03 '15 at 19:46
  • this is the way to go when dealing with table cells (where column width is determined at display time) – David J May 29 '15 at 22:13
  • 3
    @brendanmckeown if you make the width larger, you can just change the background location. So `width:26px;` and `2px 0` as background position would move it. http://jsfiddle.net/Bk38F/23/ – Jacob Raccuia Jul 17 '15 at 20:01
  • 4
    @brendanmckeown: You CAN use margin to make space between the icon and text. Use the pseudo's left margin to make the space, and make its negative right margin the sum of its width and the left margin: { width: 24px; margin-left: 10px; margin-right: -34px } – Rand Scullard Mar 25 '16 at 19:09
  • This solution isn't working. At least in Chrome I can still find screen sizes where icon is wrapping before the last word. That just don't happen so often with this solution but it's still possible. – m5seppal Sep 12 '16 at 08:24
  • Seems to work now. Did you update the fiddle as well? I tried that before and I were able to set screen sizes when icon wrapped before text. – m5seppal Oct 01 '16 at 14:34
20

This is a little similar to a previous answer, but I thought that I'd flesh it out and explain it fully.

As soon as you set display: inline-block, :after becomes a non-text-binding element. This is why it wraps, and why nowrap has no effect. So leave display alone.

As it's a text element by default, the content binds to previous text, as long as there's no whitespace between them. So don't add any, but make sure you have content: "", or it's the same as display: none. The only problem is height and width are controlled by the content, which in this case is collapsed. So width is 0, and height is the line height.

Resize the area with padding; left or right, it doesn't matter. Add a little margin so the icon doesn't touch the text. Keep everything as relative sizes (em, pt, etc.), and use background-size: contain, so that the icon scales with the text, like an emoji or font. Finally position the icon where you want with background-position.

ul li.completed a:after {
  content: "";
  background: url('icon.svg');
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center center;
  margin-left: 0.2em; /* spacing*/
  padding-right: 0.75em; /* sizing */
}

I've used this for my external links (a[href*="://"]:after), and it's worked fine in Firefox, Chrome, and iOS. And since the icon remains a text element, even direct text after it binds, so any closing punctuation will wrap too.

Phernost
  • 1,143
  • 1
  • 12
  • 8
  • This works great for `:after`. Any idea how to make it work for `:before` as well? – jor Jun 24 '19 at 14:32
19

you can add the image to the last word instead. that will make it break together.

add a class to word <strong class="test">word</strong>

and .test:after { ...

http://jsfiddle.net/52dkR/

the trick is also to make that class to be inline-block

and if you want to keep the underline see this.

http://jsfiddle.net/atcJJ/

add text-decoration:inherit;

btevfik
  • 3,391
  • 3
  • 27
  • 39
  • 4
    I don't think there will always be a last word in a strong element. – yunzen Apr 19 '13 at 09:43
  • wonderful, works like a charm indeed... @HerrSerker the strong element was just for clarity, I inserted a `` to each last word, and appended the icon to this `span` – ptriek Apr 19 '13 at 09:46
  • @HerrSerker i made the strong element into span in the second jsfiddle as well. i figured it was for clarity. – btevfik Apr 19 '13 at 09:48
  • I tried this it takes the element to the 2nd line when the word wraps. How does this work? http://jsfiddle.net/supriti/atcJJ/5/ – Supriti Panda Apr 19 '13 at 15:40
  • display: inline-block trick worked for me, adding it to the last word would have been very complicated since the content is auto-generated based on the data coming from the database. – downhand Mar 14 '16 at 10:09
  • 4
    This answer is not general enough. You expect a specific HTML tag to always be where you want it to be. – fritzmg Nov 18 '17 at 19:35
  • If you want to use this option, at least use a more semantic version of it: means there is a special semantic meaning for that last word, which is not true if you're doing this just for display. E.g. use instead of . – Christian Oct 13 '22 at 11:49
9

If using an icon font: Using the "non-breaking space" unicode \00a0 along with the pseudo-element content (in this case a Font Awesome glyph).

This works without any extra mark-up or special formatting or classnames.

a[href^='http']::after {
  content: '\00a0\f14c';
  font-family: 'Font Awesome 5 Pro', sans-serif;
  line-height: 0;
}

The line-height: 0; part keeps the lines from twitching when the last 'word + icon' wrap.

CodePen example

Jase
  • 603
  • 1
  • 6
  • 7
7

I was having the same issue. I didn't really like the idea of adding strong or span tags to the last word, so I tried simply adding the icon as a background image with some padding-right instead of using the :after or :before pseudo elements. Worked for me!

<ul><li class="completed"><a href="#">I want the icon to stay on the same line as this last word</a></li></ul>

ul li.completed a {
background: url(img1.png) no-repeat 100% 50%;
padding-right: 18px;
}

http://jsfiddle.net/atcJJ/36/

Jason
  • 71
  • 1
  • 1
5

I was attempting this with a link button and had the most success with adding padding to the right of the element as @Hugo Silva suggested, then setting the ::after to absolute position. This was a pretty simple solution and seemed to work across modern browsers.

.button {
  position: relative;
  color: #F00;
  font-size: 1.5rem;
  padding-right: 2.25rem;
}

.button::after {
  content: '>>';
  display: inline-block;
  padding-left: .75rem;
  position: absolute;
}

Here's a JS Fiddle.

Mary7678
  • 413
  • 6
  • 12
4

Please make sure you end the tag before

<ul><li class="completed"><a href="#">I want the icon to stay on the same line as this last <strong>word</strong></a></li></ul>

Try the below css. Instead of using "after" use "before" and float right.

ul li.completed a:before {
    background:transparent url(img1.png) no-repeat; 
    content: '';
    display:inline-block;
    width: 24px;
    height: 16px;
    float:right;
}

Please check this: http://jsfiddle.net/supriti/atcJJ/7/

Supriti Panda
  • 1,281
  • 1
  • 11
  • 12
3

It is possible to do it without :after. The element with background should be display:inline.

<h1><span>I want the icon to stay on the same line as this last word</span></h1>

h1 span {
   padding-right: 24px;
   background: url('icon.svg') no-repeat right top; 
}

http://jsfiddle.net/my97k7b1/

Sajib Khan
  • 22,878
  • 9
  • 63
  • 73
Svyat P.
  • 132
  • 1
  • 4
2

The problem occurs when there is whitespace at the end of the text of the element, so it considers the icon as another word. My solution is to remove the white space with javascript and put it outside the element. Also, this way we avoid that text-decoration:underline styles the white space that may be at the end of a link:

$(document).ready(function () {
  $("a[href$=\".pdf\"]").each(function () {
    var endspace = /\s$/;
    if (endspace.test($(this).html())) {
      var linktx = $(this).html().replace(/\s*$/, "");
      $(this).html(linktx);
      $(this).after(" ");
    }
  });
});

In the css I add a non-breaking space \00A0 before the icon to separate it from the text, and to scale according to the font-size. In some cases, using a narrow no-break space \202F offers a better visual effect:

a[href$=".pdf"]:after {
  content: "\00A0\f1c1";
  font-family: "Font Awesome 5 Pro";
}

https://jsfiddle.net/poselab/910bw7zt/8/

PoseLab
  • 1,841
  • 1
  • 16
  • 22
  • Wow thank you! I was struggling forever and no posts sorted my problem.. but using `content: '\00A0';` was the trick. <3 – Paul Irish Jun 26 '20 at 21:19
0

With my particular set up, the only working solution was to use position relative, and place my icon element in a span with a position absolute. Like so:

h2 a {
            position: relative;
            width: auto;
}
h2 a span {
            position: absolute;
            right: -30px;
            bottom: 0;
 }
<h2>
    <a href="#">
            The arrow won't drop to the next line as an orphan, even if the text is long and responsive
         <span>&#8594;</span>
    </a>
</h2>
Archie Butler
  • 455
  • 6
  • 20
0

I just used: white-space: nowrap; Worked for me!

David S
  • 383
  • 5
  • 6