7

I have a bunch of divs that have a content div inside of them. In the content div are 3 elements, an h1, a p and a span, all left-aligned. What I want to happen is the following:

  • The content div should be vertically and horizontally centered
  • The content div should be exactly as wide as the text in the h1 or the text in the span (whichever is longer), if above the max-width these should wrap
  • The p should be 75% as wide as the content div but not have an impact on the content div's size (effectively being 75% as wide as the h1 or span, whichever is longer)

However I'm running into the following problems:

  • Problem 1: Having a long p element causes the content div to expand to its max-width no matter the size of the h1 or span. I've tried using absolute positioning to fix this but it disrupts the vertical centering of the div
  • Problem 2: Having a long h1 element leaves a gap where the word breaks over 2 lines making the content div not appear centered

See the code snippet below to clarify what I'm after and what's going wrong, the borders are just to help visualise what's happening.

Has anyone got an idea of how this is possible? I would like to stick to CSS as these need to be responsive, although if there is a simple JS/jQuery solution it would be considered.

EDIT: To clarify the visual effect I am after here's a run-down of why the examples are good or bad. I've also added the ability to remove borders to demonstrate what I mean by something being visually centred:

1) Good: Content div fits to width of h1, looks centered without the borders as equal space to left and right of h1

2) Good: Content div fits to width of span as it's longer than the h1, looks centered without the borders as equal space to left and right of span

Problem 1:

3) Bad: p is expanding the width of the content div, looks shifted to the left without borders as more space on right than left. If the p did not expand the div and stayed at 75% of the width this would not happen

4) Improvement on 3 but still bad: Potential fix found in various SO questions showing absolute positioning stops the p expanding the content div, but now that it is not part of the flow it messes up the vertical centering

Problem 2:

5) Bad: The problem here is the h1 element, because it is now longer than the max-width it splits into 2 lines. But the extra space between the end of the first line and the max-width of the div is kept so when removing borders it doesn't look centered because there is more space to the right than the left of the h1

6) Fixes 5 but not a solution: Manually breaking the line (using a <br>) achieves the look I need, because the h1 isn't expanded to the max-width so looks centered without the borders. This isn't feasible in the real application though because the divs can vary in width

Alternative JSFiddle Link

function toggleBorders() {
  $('h1, p, span').toggleClass('bordered');
  $('.content').toggleClass('content-bordered');
}
.box-holder {
  display: flex;
  flex-flow: row wrap;
}
.box {
  flex: 0 0 380px;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 350px;
  border: 1px solid blue;
}
.content {
  max-width: 80%;
  position: relative;
}
.content-bordered {
  border: 1px solid red;
}
.bordered {
  border: 1px solid green;
}
p {
  width: 75%;
}
.abs {
  position: absolute;
}
button {
  position: fixed;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button onclick="toggleBorders()">Toggle Borders</button>
<div class="box-holder">
  <div class="box">
    <div class="content">
      <h1>1. Example Title</h1>
      <p>Good Example</p>
      <span>Example link to the article</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>2. Title</h1>
      <p>Min Width Good Example</p>
      <span>Example link to the article</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>3. Example Title</h1>
      <p>But when the description gets too long then it expands the content div</p>
      <span>Example link to the article</span>
    </div>
  </div>
   <div class="box">
    <div class="content">
      <h1>4. Example Title</h1>
      <p class="abs">Setting absolute position avoids expansion but messes up the vertical layout</p>
      <span class="abs">Example link to the article</span>
    </div>
  </div>
   <div class="box">
    <div class="content">
      <h1>5. Also Long Titles Leave White Space</h1>
      <p>This doesn't look centered, see 6</p>
      <span>Example link to the article</span>
    </div>
  </div>
   <div class="box">
    <div class="content">
      <h1>6. Also Long Titles<br>Leave White Space</h1>
      <p>Manually breaking lines fixes this</p>
      <span>Example link to the article</span>
    </div>
  </div>
</div>
BenShelton
  • 901
  • 1
  • 8
  • 20

2 Answers2

1

So the answer to both of these questions, it seems, is that you cannot do this without javascript. The reason being that the CSS box model just does not work in this way.

In order to solve just the first problem you need to use absolute positioning like I tried but then use javascript to create a space for the element using a margin on the h1, something like this:

$(document).ready(function() {
    function alignDescriptions() {
    var pmargin = 10 * 2;
    $('.abs').each(function() {
      var pheight = $(this).height();
      $(this).css('bottom', pmargin);
      $(this).siblings('h1').css('margin-bottom', pheight + pmargin);
    });
  }
});

That solves the vertical centering issue when using absolute so problem 1 is fixed.

To solve the second problem, the following answer provides one solution: https://stackoverflow.com/a/33246364/7542390

I believe simply using this but also using the width of the span as a minimum would probably solve both problems as I would be literally forcing the width to the correct size so having a 75% width p element wouldn't be a problem.

It's a shame that this kind of functionality isn't in the CSS spec.

EDIT: As suspected, an adaption of the second option actually removes the need for absolute positioning of the p element. Here's the jQuery code that worked for my actual case:

$('h1').each(function() {
  // references to elements
  var hElem = $(this);
  var pElem = hElem.siblings('p');
  var sElem = hElem.siblings('span');
  // store starting values
  var sWidth = sElem.width();
  var hHeight = hElem.height();
  var hWidth = hElem.width();
  // reduce width until less than span width
  // or until height of h1 changes
  for (var testWidth = hWidth - 1; testWidth > 0; testWidth--) {
    if (testWidth <= sWidth) {
      testWidth = sWidth - 1;
      break;
    }
    hElem.width(testWidth);
    if (hElem.height() !== hHeight) break;
  }
  // set h1 width
  hElem.width(++testWidth);
  // if h1 still overflows (long word) use that instead
  if (hElem[0].scrollWidth > hElem.width()) {
    testWidth = hElem[0].scrollWidth;
    hElem.width(testWidth);
  }
  // set p element to 75% width
  pElem.width(testWidth * 0.75);
});
Community
  • 1
  • 1
BenShelton
  • 901
  • 1
  • 8
  • 20
-1

Your question is very interesting.I have solved your div expansion of the description by setting the max-width as the same as your min-width. Then it cannot expand.

I can't figure out the problem with the white space and title. It looks centred to me.

Here is what I got:

.box-holder {
  display: flex;
  flex-flow: row wrap;
}

.box {
  flex: 1 0 350px;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 350px;
  border: 1px solid blue;
}

.content {
  min-width: 50%;
  max-width: 50%;
  border: 1px solid red;
}

h1,
p,
span {
  border: 1px solid green;
}

p {
  width: 75%;
}

.abs {
  position: absolute;
}
<div class="box-holder">
  <div class="box">
    <div class="content">
      <h1>1. Example Title</h1>
      <p>Good Example</p>
      <span>Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>2. Title</h1>
      <p>Min Width Good Example</p>
      <span>Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>3. Example Title</h1>
      <p>When the description gets too long then it DOESN'T expand the content div</p>
      <span>Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>4. Example Title Size</h1>
      <p class="abs">Setting absolute position avoids expansion but messes up the vertical layout</p>
      <span class="abs">Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>5. Also Long Titles Leave White Space</h1>
      <p>This does look centered</p>
      <span>Link</span>
    </div>
  </div>
</div>

I hope it helps and this is what you're looking for.

EDIT

Solving 5 with text-align: justify;

.box-holder {
  display: flex;
  flex-flow: row wrap;
}

.box {
  flex: 1 0 350px;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 350px;
  border: 1px solid blue;
}

.content {
  min-width: 50%;
  max-width: 50%;
  border: 1px solid red;
}

h1,
p,
span {
  border: 1px solid green;
}

p {
  width: 75%;
}

.abs {
  position: absolute;
}


/*NEW CSS:*/

h1 {
  text-align: justify;
}
<div class="box-holder">
  <div class="box">
    <div class="content">
      <h1>1. Example Title</h1>
      <p>Good Example</p>
      <span>Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>2. Title</h1>
      <p>Min Width Good Example</p>
      <span>Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>3. Example Title</h1>
      <p>When the description gets too long then it DOESN'T expand the content div</p>
      <span>Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>4. Example Title Size</h1>
      <p class="abs">Setting absolute position avoids expansion but messes up the vertical layout</p>
      <span class="abs">Link</span>
    </div>
  </div>
  <div class="box">
    <div class="content">
      <h1>5. Also Long Don't Titles Leave White Space</h1>
      <p>This does look centered</p>
      <span>Link</span>
    </div>
  </div>
</div>
Sank6
  • 491
  • 9
  • 28
  • Unfortunately this doesn't solve the problem. I need the div to expand to the width of the header. Also, example 5 is technically centered yes, but when you remove the borders the text will not look centered because there's more space to the right of the title than the left. – BenShelton May 06 '17 at 17:38
  • But you wanted it to be centred. You can't have centrally aligned left-aligning text. – Sank6 May 06 '17 at 18:12
  • I want left-aligned text in a visually centered div. I've edited my initial post to add an example 6, you see the difference when you eliminate the extra white-space generated when breaking a line? – BenShelton May 06 '17 at 18:20
  • I see. wait a min. I will do. – Sank6 May 06 '17 at 18:25
  • Just changing the text alignment isn't going to fix the problem, I need to eliminate the white space generated when an element breaks onto another line, can you solve that? – BenShelton May 06 '17 at 18:43
  • That's exactly what the text align justify does. – Sank6 May 06 '17 at 20:59
  • See my edits to question above as to why this doesn't answer my question. – BenShelton May 07 '17 at 08:42