12

I've got a procedure that consists of steps. Some steps are accompanied by an image.

    <p class="imagefloatright"><img src="step 1.png"/></p>
    <ol>
        <li>
          <p>Step 1</p>
          <p class="imagefloatright"><img src="step 2.png"/></p>
        </li>
        <li>
          <p>Step 2</p>
          <p>Step 3</p>
        </li>
    </ol>

and my CSS:

p.imagefloatright img {
    float: right;
    clear: both;
}

This is the default output. Images don't stay with the steps they belong to, the text for step 2 is put alongside image 1:

enter image description here

I want the image that belongs to step 2 to be vertically aligned with step 2:

enter image description here

In the past, I've achieved my desired result in XSL-FO by inserting a full-width block with height =0 before each floated image.

Can I achieve my desired layout using CSS commands? Or do I need to insert a block in the HTML before I apply the CSS to the HTML?

Hobbes
  • 1,964
  • 3
  • 18
  • 35

10 Answers10

2

To clear the float which contains the dynamic content ie. image can have dynamic height, you'll need to clear the parent element itself.

.imagefloatright {
  clear: both;
}
.imagefloatright img {
  float: right;
}

It means that you need to use clear on the element which contains the floating elements.


For brevity, I would rename it like:

.clearfix {
  clear: both;
}
.float-right {
  float: right;
}

HTML

<p class="clearfix"><img class="float-right" src="step 1.png"/></p>
<ol>
    <li>
      <p>Step 1</p>
      <p class="clearfix"><img class="float-right" src="step 2.png"/></p>
    </li>
    <li>
      <p>Step 2</p>
      <p>Step 3</p>
    </li>
</ol>

Here's a demo.

Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231
1

You should use clear property not on p element, but create another element in the place where you would like to stop float.

p.imagefloatright img {
    float: right;
}

.clear {
    clear:both;
}
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 1</p>
<div class="clear"></div>
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 2</p>
<div class="clear"></div>

By the way, here is a code snippet showing a better layout for your steps (from my point of view), which is more logical, each step is a block with image and text positioned correctly.

p.imagefloatright {
    clear:both;
}

p.imagefloatright img {
    float: right;
}
<p class="imagefloatright">
  Step 1
   <img src="http://via.placeholder.com/80x80"/>
</p>

<p class="imagefloatright">
  Step 2
  <img src="http://via.placeholder.com/80x80"/>
</p>

If you still would like to have text in a separate p element you may have all steps as a div elements with properly styled paragraph and images inside. I am also applying display:inline-block on p element to prevent it from taking whole width. You can do same or use span instead of p.

.imagefloatright {
    clear:both;
}

.imagefloatright p {
    display: inline-block;
    margin: 0;
}

.imagefloatright img {
    float: right;
}
<div class="imagefloatright">
  <p>Step 1</p>
   <img src="http://via.placeholder.com/80x80"/>
</div>

<div class="imagefloatright">
  <p>Step 2</p>
  <img src="http://via.placeholder.com/80x80"/>
</div>
Artem Arkhipov
  • 7,025
  • 5
  • 30
  • 52
  • But why? The clear property was designed to be able to be used without a dedicated clearing element - a replacement for
    .
    – BoltClock Sep 07 '18 at 08:25
  • It should be after floating element and not before. I will extend my answer with another example right now. – Artem Arkhipov Sep 07 '18 at 08:26
  • Yes, you don't need a separate element for that - you can just apply the clear property to whichever comes after where that element would be. – BoltClock Sep 07 '18 at 08:31
  • Agree, but in authors case he has something like a list. So after each `step` he has another one with same CSS and float. That's why I decided to add element between steps. – Artem Arkhipov Sep 07 '18 at 08:33
  • I do have a list. My example was too simplified, I'll make it more complete. – Hobbes Sep 07 '18 at 09:02
  • I placed the image before the step because I want the image and step to be aligned. If I place the step first, and the image after it, the image top edge aligns with the bottom edge of the step. – Hobbes Sep 07 '18 at 09:17
  • @Hobbes that happens because `p` element takes whole width as it is block element. Change its `display` property to `inline-block` and remove margin as in my third snippet. – Artem Arkhipov Sep 07 '18 at 10:31
1

If every step that has an image will only have exactly one image, and an image isn't going to be shared by more than one step, you can write a selector to have the following step clear each floating image but it's very rigid:

p.imagefloatright img {
  clear: both;
  float: right;
}

p.imagefloatright + p:not(.imagefloatright) + p {
  clear: both;
}
<p class="imagefloatright"><img src="https://placehold.it/100x100"/></p>
<p>Step 1</p>
<p class="imagefloatright"><img src="https://placehold.it/100x100"/></p>
<p class="imagefloatright"><img src="https://placehold.it/100x100"/></p>
<p>Step 2</p>
<p>Step 3</p>

If there isn't a strict 1-to-1 relationship between steps and images then you will need to apply the clear property strategically to specific steps.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • the relationship is not 1:1 in this case. – Hobbes Sep 07 '18 at 08:42
  • @Hobbes: If that means there will be some steps that don't have images, this will still work. If some steps will have more than one image but no image will be shared between steps, the selector `p.imagefloatright + p:not(.imagefloatright) + p` should work. But in general it is very difficult to wrangle floats when all of your elements are participating in the same flow and you don't have a way to delimit steps. Many of the other answers are suggesting restructuring your markup which is indeed the cleanest solution. – BoltClock Sep 07 '18 at 09:02
1

Here is an easy way with flexbox:

ol {
  list-style: none;
  padding:0;
  margin:0;
}

li {
  display: flex;
  /*for illustration*/
  border:1px solid;
  padding:5px;
}

img {
  margin-left: auto;
}
<ol>
  <li>
    Step 1
    <img src="https://placehold.it/100x100">
  </li>
  <li>
    Step 2
  </li>
  <li>
    Step 3
  </li>
  <li>
    Step 4
    <img src="https://placehold.it/100x100">
  </li>
</ol>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
1

Solution 1: Using Flex

I would recommend using flex and justify-content: space-between the only drawback for using it would be if you are supporting older browsers such IE10- then you can check https://caniuse.com/#search=flex for more info.

As for space-between is simple as according to MDN

The items are evenly distributed within the alignment container along the main axis. The spacing between each pair of adjacent items is the same. The first item is flush with the main-start edge, and the last item is flush with the main-end edge.

Meaning, it will have a first child aligned to the beginning of the parent and the last child aligned to the end and the remaining space is divided equally among the rest of the children if they exist, which will give you the desired layout if the img is always the last child, no matter how many child divs exist.

Personally, I would find using flex is the most convenient and one of the cleanest ways to "position" and "layout" different elements across the page.

You can find the code below and a working fiddle here

HTML

<ol class="step-list">
  <li class="step">
    <p>Step 1</p>
    <img src="https://placehold.it/150x150?text=Step1" alt="">
  </li>
  <li class="step">
    <p>Step 2</p>
  </li>
  <li class="step">
    <p>Step 3</p>
  </li>
  <li class="step">
    <p>Step 4</p>
    <img src="https://placehold.it/150x150?text=Step4" alt="">
  </li>
</ol>

CSS

.step-list {
  list-style: none;
}

.step {
  display: flex;
  justify-content: space-between;
}

Solution 2: Using Float

You can also still be using float but in that case you will need to clear the p instead of the img and float it too.

Since all of the element of the containing elements are floated it is important to add a clearfix to the li tag not to collapse to nothing you can find more info here.

So your code would like this and you can find a working fiddle here

HTML

<ol class="step-list">
  <li class="step">
    <p>Step 1</p>
    <img src="http://placehold.it/150x150?text=Step1" alt="">
  </li>
  <li class="step">
    <p>Step 2</p>
  </li>
  <li class="step">
    <p>Step 3</p>
  </li>
  <li class="step">
    <p>Step 4</p>
    <img src="http://placehold.it/150x150?text=Step4" alt="">
  </li>
</ol>

CSS

.step-list {
  list-style: none;
}

.step img {
  float: right;
}

.step p {
  float: left;
}

/*This is what is called a clearfix solution, for the floating problem*/
.step::after {
  content: "";
  display: block; 
  clear: both;
}

Don't hesitate to ask me any more questions if anything unclear.

Rawan Bekheet
  • 99
  • 1
  • 5
0

clearfix should help. :

.clearfix:after {
  display: block;
  content: "";
  clear: both;
}

And the html:

<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p class="clearfix">Step 1</p>
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p class="clearfix">Step 2</p>
<p>Step 3</p>
n1stre
  • 5,856
  • 4
  • 20
  • 41
0

Why not with a pseudo element?

p, .imagefloatright img { margin: 0 0 .25em; }

.imagefloatright img {
  float: right;
}

.imagefloatright::before {
  clear: both;
  content: '';
  display: block;
}
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 1</p>
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 2</p>
<p>Step 3</p>
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 4</p>
<p>Step 5</p>

You have to play around with ::before and ::after pseudo elements. Another solution could be the following:

p, .imagefloatright img { margin: 0 0 .25em; }

.imagefloatright img {
  float: right;
}

.imagefloatright + p::after {
  clear: both;
  content: '';
  display: block;
}
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 1</p>
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 2</p>
<p>Step 3</p>
<p class="imagefloatright"><img src="http://via.placeholder.com/80x80"/></p>
<p>Step 4</p>
<p>Step 5</p>

Should all the following steps belongs to the image or shouldn't? It depends ;)

RWAM
  • 6,760
  • 3
  • 32
  • 45
  • That works if there is a single step after the image, but what if after step 3 there's another image that belongs to step 4 and 5? – Hobbes Sep 07 '18 at 09:14
  • Hi @Hobbes, I've updated the example to demonstrate that it works with more items than 3 too. – RWAM Sep 07 '18 at 10:11
0

You can use this

 <p>
    <span class="left">1</span>
       <img class="right"  src="http://via.placeholder.com/90x90" alt="">
  </p>

   <p>
    <span class="left">2</span>
       <img class="right"  src="http://via.placeholder.com/90x90" alt="">
  </p>

p{
  display:inline-block;
    margin-bottom:5px;
    width:100%;
}
p .left{
  float:left;
  margin-bottom:55px;
}
p .right{
 float:right;
}

https://jsfiddle.net/jlbarcelona/vcmdo7uy/106/

JL Barcelona
  • 230
  • 1
  • 6
0

It is very easy to do that. When you use css float property you must use overflow with its parent element. In your situation:

p.imagefloatright{
   overflow: auto;
}

must fix the issue. Read more about https://www.w3schools.com/css/css_float.asp

Alisher Musurmonv
  • 815
  • 1
  • 9
  • 18
0

You can use an after pseudoelement in li tag to get the height of the floated elements. Also you have to specify that you only want to float the elements who are inside the li tag using the > selector.

ol {
  padding: 0;
}

li {
  padding: 10px;
  list-style: none;
  background: #eee;
  margin: 10px 0;
  width: auto;
}

li:after {
  content: '';
  display: table;
  clear: both;
}

li > p {
  width: 25%;
  float: left;
}

p {
  margin: 0;
}
<p><img src="http://fpoimg.com/300x300"/></p>
<ol>
  <li>
    <p>Step 1</p>
    <p><img src="http://fpoimg.com/300x300?text=Step1"/></p>
  </li>
  <li>
    <p>Step 2</p>
    <p>Step 3</p>
  </li>
</ol>
Nacorga
  • 507
  • 4
  • 17