Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Div expand to size of certain content

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>
like image 238
BenShelton Avatar asked May 06 '17 17:05

BenShelton


People also ask

How dynamically change the height of a div?

The content height of a div can dynamically set or change using height(), innerHeight(), and outerHeight() methods depending upon the user requirement.

How do I make div width auto adjust?

Using width, max-width and margin: auto; Then, you can set the margins to auto, to horizontally center the element within its container. The element will take up the specified width, and the remaining space will be split equally between the two margins: This <div> element has a width of 500px, and margin set to auto.

How do you fit content and height in CSS?

If height: auto; the element will automatically adjust its height to allow its content to be displayed correctly. If height is set to a numeric value (like pixels, (r)em, percentages) then if the content does not fit within the specified height, it will overflow.


1 Answers

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);
});
like image 100
BenShelton Avatar answered Sep 18 '22 01:09

BenShelton