270

What's the easiest way to detect if an element has been overflowed?

My use case is, I want to limit a certain content box to have a height of 300px. If the inner content is taller than that, I cut it off with an overflow. But if it is overflowed I want to show a 'more' button, but if not I don't want to show that button.

Is there an easy way to detect overflow, or is there a better method?

T J
  • 42,762
  • 13
  • 83
  • 138
Harry
  • 52,711
  • 71
  • 177
  • 261
  • 3
    If someone is looking for a custom React Hook to perform this task in React land: https://www.robinwieruch.de/react-custom-hook-check-if-overflow – Robin Wieruch Dec 21 '21 at 22:01

16 Answers16

365

The element may be overflown vertically, horizontally or both. This function will return you a boolean value if the DOM element is overflown:

function isOverflown(element) {
  return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

function isOverflown(element) {
  return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

var els = document.getElementsByClassName('demos');
for (var i = 0; i < els.length; i++) {
  var el = els[i];
  el.style.borderColor = (isOverflown(el) ? 'red' : 'green');
  console.log("Element #" + i + " is " + (isOverflown(el) ? '' : 'not ') + "overflown.");
}
.demos {
  white-space: nowrap;
  overflow: hidden;
  width: 120px;
  border: 3px solid black;
}
<div class='demos'>This is some text inside the div which we are testing</div>
<div class='demos'>This is text.</div>

ES6 example:

const isOverflown = ({ clientWidth, clientHeight, scrollWidth, scrollHeight }) => {
    return scrollHeight > clientHeight || scrollWidth > clientWidth;
}
ashleedawg
  • 20,365
  • 9
  • 72
  • 105
micnic
  • 10,915
  • 5
  • 44
  • 55
  • 47
    Great solution! Here's a jQuery variant (to be used with `$("#element").overflown()`): `$.fn.overflown=function(){var e=this[0];return e.scrollHeight>e.clientHeight||e.scrollWidth>e.clientWidth;}` – Sygmoral Aug 22 '13 at 22:01
  • @micnic hi , I am using ionic2 and trying to do the same thing... but the function returns true always... is there a different way to do things in angular 2 – Yasir Apr 23 '17 at 09:31
  • @DavidBracoly, this function is for pure DOM API, I do not have experience with Ionic/Angular, I guess you should get the original DOM Element and use this function – micnic Apr 23 '17 at 10:27
  • 4
    Only for Firefox: `element.addEventListener ("overflow", () => log( "overflow" ), false);` Reference: [overflow event](https://developer.mozilla.org/en-US/docs/Web/Events/overflow) – Reza Feb 18 '19 at 11:13
  • 2
    Wow, this even works e.g. in a `grid`, where the bounding client rect doesn’t include overflowing content. – Sebastian Simon May 24 '21 at 06:15
  • @Reza This page is no longer on MDN and it the event does not work in Firefox 116 – Petr L. Jul 03 '23 at 10:15
141

If you want to show only an identifier for more content, then you can do this with pure CSS. I use pure scrolling shadows for this. The trick is the use of background-attachment: local;. Your css looks like this:

.scrollbox {
  overflow: auto;
  width: 200px;
  max-height: 200px;
  margin: 50px auto;

  background:
    /* Shadow covers */
    linear-gradient(white 30%, rgba(255,255,255,0)),
    linear-gradient(rgba(255,255,255,0), white 70%) 0 100%,
    
    /* Shadows */
    radial-gradient(50% 0, farthest-side, rgba(0,0,0,.2), rgba(0,0,0,0)),
    radial-gradient(50% 100%,farthest-side, rgba(0,0,0,.2), rgba(0,0,0,0)) 0 100%;
  background:
    /* Shadow covers */
    linear-gradient(white 30%, rgba(255,255,255,0)),
    linear-gradient(rgba(255,255,255,0), white 70%) 0 100%,
    
    /* Shadows */
    radial-gradient(farthest-side at 50% 0, rgba(0,0,0,.2), rgba(0,0,0,0)),
    radial-gradient(farthest-side at 50% 100%, rgba(0,0,0,.2), rgba(0,0,0,0)) 0 100%;
  background-repeat: no-repeat;
  background-color: white;
  background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;
  
  /* Opera doesn't support this in the shorthand */
  background-attachment: local, local, scroll, scroll;
}
<div class="scrollbox">
  <ul>
    <li>Not enough content to scroll</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
  </ul>
</div>


<div class="scrollbox">
  <ul>
    <li>Ah! Scroll below!</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>The end!</li>
    <li>No shadow there.</li>
  </ul>
</div>

The code and the example you can find on http://dabblet.com/gist/2462915

And an explanation you can find here: http://lea.verou.me/2012/04/background-attachment-local/.

RWAM
  • 6,760
  • 3
  • 32
  • 45
  • 2
    Nice idea but in your example the background (shadow) would be behind the text! – DreamTeK Apr 30 '18 at 14:59
  • Great trick, however in Chrome it shows only static shadow on the bottom, it never disappears and the upper shadow never shows – Jared Jun 06 '18 at 14:50
  • _/* Opera doesn't support this in the shorthand */_ -> I'm on Opera and it seems to work for me. – borisdiakur Jul 16 '21 at 11:11
  • This works in Chrome v114 for me now. The shadow may be behind the text, but in this example it's not apparent because both the text and shadow are the same colour. – David Cook Jun 16 '23 at 03:51
27

Comparing element.scrollHeight to element.clientHeight should do the task.

Below are the images from MDN explaining Element.scrollHeight and Element.clientHeight.

Scroll Height

Client Height

Gowtham Raj J
  • 937
  • 9
  • 26
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 2
    Yeah, but according to [quirksmode](http://quirksmode.org/dom/w3c_cssom.html#elementview) it works for the most - and is much simpler than the other posted solutions. – Bergi Feb 17 '12 at 18:50
  • @Harry: It's supported in all current browsers (desktop ones at least), so it doesn't matter that it's formally nonstandard. – Marat Tanalin Mar 07 '12 at 23:03
  • 1
    Here, these properties always have the same values. – koppor Aug 25 '13 at 19:27
12

I made a multipart codepen demonstrating the above answers ( e.g. using overflow hidden and height) but also how you would expand / collapse overflowed items

Example 1: https://codepen.io/Kagerjay/pen/rraKLB ( Real simple example, no javascript, just to clip overflowed items)

Example 2: https://codepen.io/Kagerjay/pen/LBErJL (Single event handler show more / showless on overflowed items)

Example 3: https://codepen.io/Kagerjay/pen/MBYBoJ (Multi event handler on many show more/ show less on overflowed items)

I have attached example 3 below as well, I use Jade/Pug so it might be a tad verbose. I suggest checking out the codepens I've made its simpler to grasp.

// Overflow boolean checker
function isOverflown(element){
  return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

// Jquery Toggle Text Plugin
$.fn.toggleText = function(t1, t2){
  if (this.text() == t1) this.text(t2);
  else                   this.text(t1);
  return this;
};

// Toggle Overflow
function toggleOverflow(e){
  e.target.parentElement.classList.toggle("grid-parent--showall");
  $(e.target).toggleText("Show More", "Show LESS");
}

// Where stuff happens
var parents = document.querySelectorAll(".grid-parent");

parents.forEach(parent => {
  if(isOverflown(parent)){
    parent.lastElementChild.classList.add("btn-show");
    parent.lastElementChild.addEventListener('click', toggleOverflow);
  }
})
body {
  background-color: #EEF0ED;
  margin-bottom: 300px;
}

.grid-parent {
  margin: 20px;
  width: 250px;
  background-color: lightgrey;
  display: flex;
  flex-wrap: wrap;
  overflow: hidden;
  max-height: 100px;
  position: relative;
}
.grid-parent--showall {
  max-height: none;
}

.grid-item {
  background-color: blue;
  width: 50px;
  height: 50px;
  box-sizing: border-box;
  border: 1px solid red;
}
.grid-item:nth-of-type(even) {
  background-color: lightblue;
}

.btn-expand {
  display: none;
  z-index: 3;
  position: absolute;
  right: 0px;
  bottom: 0px;
  padding: 3px;
  background-color: red;
  color: white;
}

.btn-show {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
  <p>Any grid-parent over 10 child items has a "SHOW MORE" button to expand</p>
  <p>Click "SHOW MORE" to see the results</p>
</section>
<radio></radio>
<div class="wrapper">
  <h3>5 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>8 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>10 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>13 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>16 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
  <h3>19 child elements</h3>
  <div class="grid-parent">
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="grid-item"></div>
    <div class="btn-expand">Show More</div>
  </div>
</div>
Vincent Tang
  • 3,758
  • 6
  • 45
  • 63
5

Would something like this: http://jsfiddle.net/Skooljester/jWRRA/1/ work? It just checks the height of the content and compares it to the height of the container. If it's greater than you can put in the code to append a "Show more" button.

Update: Added the code to create a "Show More" button at the top of the container.

ayyp
  • 6,590
  • 4
  • 33
  • 47
3

I've extended Element for encapsulation reasons. From micnic answer.

/*
 * isOverflowing
 * 
 * Checks to see if the element has overflowing content
 * 
 * @returns {}
 */
Element.prototype.isOverflowing = function(){
    return this.scrollHeight > this.clientHeight || this.scrollWidth > this.clientWidth;
}

Use it like this

let elementInQuestion = document.getElementById("id_selector");

    if(elementInQuestion.isOverflowing()){
        // do something
    }
David Clews
  • 795
  • 6
  • 14
3

You can check the bounds relative to the offset parent.

// Position of left edge relative to frame left courtesy
// http://www.quirksmode.org/js/findpos.html
function absleft(el) {
  var x = 0;
  for (; el; el = el.offsetParent) {
    x += el.offsetLeft;
  }
  return x;
}

// Position of top edge relative to top of frame.
function abstop(el) {
  var y = 0;
  for (; el; el = el.offsetParent) {
    y += el.offsetTop;
  }
  return y;
}

// True iff el's bounding rectangle includes a non-zero area
// the container's bounding rectangle.
function overflows(el, opt_container) {
  var cont = opt_container || el.offsetParent;
  var left = absleft(el), right = left + el.offsetWidth,
      top = abstop(el), bottom = top + el.offsetHeight;
  var cleft = absleft(cont), cright = cleft + cont.offsetWidth,
      ctop = abstop(cont), cbottom = ctop + cont.offsetHeight;
  return left < cleft || top < ctop
      || right > cright || bottom > cbottom;
}

If you pass this an element it will tell you whether its bounds are entirely inside a container, and will default to the element's offset parent if no explicit container is provided.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
3

If you are using jQuery, you might try a trick: make outer div with overflow: hiddenand inner div with content. Then use .height() function to check if height of the inner div is greater than the height of the outer div. I'm not sure it will work but give it a try.

Tomas
  • 57,621
  • 49
  • 238
  • 373
3

Another issue you should consider is a JS unavailability. Think about progressive enhancement or graceful degradation. I would suggest:

  • adding "more button" by default
  • adding overflow rules by default
  • hiding button and if necessary CSS modifications in JS after comparing element.scrollHeight to element.clientHeight
MFix
  • 229
  • 1
  • 5
2

Here is a fiddle for determining whether an element has been overflowed using a wrapper div with overflow:hidden and JQuery height() to measure the difference between the wrapper and an inner content div.

outers.each(function () {
    var inner_h = $(this).find('.inner').height();
    console.log(inner_h);
    var outer_h = $(this).height();
    console.log(outer_h);
    var overflowed = (inner_h > outer_h) ? true : false;
    console.log("overflowed = ", overflowed);
});

Source: Frameworks & Extensions on jsfiddle.net

Jean
  • 7,623
  • 6
  • 43
  • 58
1
setTimeout(function(){
    isOverflowed(element)           
},500)

function isOverflowed(element){
    return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}

This was worked for me. Thank you.

Krunal
  • 725
  • 7
  • 15
1

This is the jQuery solution that worked for me. clientWidth etc. didn't work.

function is_overflowing(element, extra_width) {
    return element.position().left + element.width() + extra_width > element.parent().width();
}

If this doesn't work, ensure that elements' parent has the desired width (personally, I had to use parent().parent()). position is relative to the parent. I've also included extra_width because my elements ("tags") contain images which take small time to load, but during the function call they have zero width, spoiling the calculation. To get around that, I use the following calling code:

var extra_width = 0;
$(".tag:visible").each(function() {
    if (!$(this).find("img:visible").width()) {
        // tag image might not be visible at this point,
        // so we add its future width to the overflow calculation
        // the goal is to hide tags that do not fit one line
        extra_width += 28;
    }
    if (is_overflowing($(this), extra_width)) {
        $(this).hide();
    }
});

Hope this helps.

Dennis Golomazov
  • 16,269
  • 5
  • 73
  • 81
1

use js to check if the child's offsetHeight is more than the parents.. if it is,make the parents overflow scroll/hidden/auto whichever you want and also display:block on the more div..

Vivek Chandra
  • 4,240
  • 9
  • 33
  • 38
0

The jquery alternative to the answer is to use the [0] key to access the raw element such as this:

if ($('#elem')[0].scrollHeight > $('#elem')[0].clientHeight){
Antony
  • 3,875
  • 30
  • 32
0

Add an event listener for conditional visibility:

document.getElementById('parent').addEventListener('scroll', (e) => {
  const childEl = document.getElementById('child');

  childEl.style.visibility = e.target.scrollTop > 0 ? 'visible' : 'hidden';
});
MVersteeg
  • 1
  • 1
0

If someone is looking for an alternative solution to detect the overflow, in reactjs, this might be useful.

export default function App() {
  const [zoom, setZoom] = useState(1);
  const [isOverFlowing, setIsOverFlowing] = useState(false);
  const innerDiv = useRef(null);
  const outerDiv = useRef(null);
  useEffect(() => {
    if (
      innerDiv.current.getBoundingClientRect().height >
        outerDiv.current.getBoundingClientRect().height ||
      innerDiv.current.getBoundingClientRect().width >
        outerDiv.current.getBoundingClientRect().width
    ) {
      setIsOverFlowing(true);
    } else {
      setIsOverFlowing(false);
    }
  }, [zoom, isOverFlowing]);
  return (
    <div className="App">
      <button
        onClick={() => {
          setZoom(zoom + 0.5);
        }}
      >
        Scale +
      </button>
      <button
        onClick={() => {
          setZoom(zoom - 0.5);
        }}
      >
        Scale -
      </button>
      <div
        ref={outerDiv}
        style={{
          width: "100vw",
          height: "100vh",
          backgroundColor: "red",
          overflow: "auto",
          display: "flex",
          justifyContent: "center",
          alignItems: "center"
        }}
      >
        <div
          ref={innerDiv}
          style={{
            width: "100px",
            height: "100px",
            backgroundColor: "green",
            transform: `scale(${zoom})`,
            transformOrigin: `${isOverFlowing ? "0 0" : "center"}`
          }}
        ></div>
      </div>
    </div>
  );
}