htmlcssoverflowcrophidden

How to hide element overflow without cropping inline images/text?


I have a container div with property overflow: hidden and a max-height of 100px. This container element contains an arbitrary amount of inline elements including both images and stylized text.

Currently, if more than 100px of content is present, then the rest of the content is clipped. This is the general behavior I want, except I keep running into issues where portions of lines of text are clipped (i.e. the bottom half of letters would just be cut off) or half of an image will be cropped out.

I found a workaround to the text issue by manually setting the column-width property, but I cannot find any way to stop the image from being cropped. If any individual line of text or image cannot be rendered in its entirety inside of the container div, I want the entire image/line-of-text to be hidden.

In summary: I have a div element that wraps a bunch of inline elements (primarily text and images) and hides any overflow and I want to not display any lines of text or images that it would crop/clip some portions of. I have found a workaround for the text issue, but not for images. How can I achieve my desired functionality?

EDIT: Per commenter request, I am posting my, albeit trivial, code:

HTML:

<div id="test">
  <p>
    This is a test. This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.This is a test.
  </p>
  <img src="https://hackernoon.com/hn-images/0*xMaFF2hSXpf_kIfG.jpg" alt="Girl in a jacket" width="500" height="600">
</div>

The inline elements will just be text and images wrapped in the appropriate tags (their actual values are dynamic and change depending on the situation. The content I have provided here is just an example.

CSS:

#test { 
  max-height: 200px;
  max-width:200px;
  overflow: hidden; 
}

Here is a JS fiddle for this example. As you can see, the image gets cropped.

Here is another JS Fiddle Example where the text itself gets clipped halfway through the line.

In the first example, I want the image to not be displayed at all.

In the second, I want the last line to not be displayed at all.

Elements that cannot be displayed in their entirety should just not be displayed. I am having particular trouble getting this desired behavior for images, so any help there would be particularly appreciated.


Solution

  • You can achieve the desired behavior using JavaScript. In this snippet I'm just giving "hidden" items a low opacity, for illustration purposes. Obviously, you could simply set the hidden items to visibility: hidden or display: none in a real-world application.

    const containers = document.querySelectorAll('.container');
    containers.forEach(container => hideOverflow(container));
    
    // Hide all overflowing images and portions of text nodes
    function hideOverflow(container) {
      container.childNodes.forEach(node => {
        if (node.nodeName !== '#text') {
          if (node.nodeName === 'IMG' ||
              node.querySelector('img') != null) {
            // Hide entire image if it overflows
            if (node.offsetTop + node.offsetHeight > container.offsetHeight) {
              node.classList.add('hidden');
            }
          }
          else {
            // Hide overflowing text characters
            if (node.offsetTop > container.offsetHeight) {
              node.classList.add('hidden');
            }
            else {
              hideChildOverflow(node, container);
            }
          }
        }
      });
    }
    
    // Find all descendant text nodes, and hide their overflowing characters
    function hideChildOverflow(node, container) {
      if (node.nodeName === '#text') {
        const overflow = document.createRange();
        overflow.setEnd(node, node.length);
        
        // Shrink range until it includes only overflowing characters
        for (let i = 0; i <= node.length; i++) {
          overflow.setStart(node, i);
          
          if (overflow.getBoundingClientRect().top > container.offsetHeight) {
            break;
          }
        }
        
        // Hide the overflowing characters
        const span = document.createElement('span');
        span.classList.add('hidden');
        overflow.surroundContents(span);
      }
      else {
        node.childNodes.forEach(childNode => {
          hideChildOverflow(childNode, container);
        });
      }
    }
    body {
      display: flex;
    }
    
    .container {
      max-height: 200px;
      max-width: 200px;
      border: 1px solid red;
      margin-right: 20px;
    }
    
    .hidden {
      opacity: .2;
    }
    <div class="container">
      <img src="https://picsum.photos/id/237/200/50" width="200" height="50" />
      <p>All of this text is visible, since none of it overflows the container.</p>
      <img src="https://picsum.photos/id/237/200/100" width="200" height="100" />
      <p>This text is below the container, so it gets hidden.</p>
    </div>
    
    <div class="container">
      <img src="https://picsum.photos/id/237/200/100" width="200" height="100" />
      <p>The start of this text is visible, but the last part gets hidden since it overflows the bottom of the container. Any piece of text that overflows, even if only partially, gets hidden.</p>
    </div>