75

How can I trigger an event when a div changes its height or any css attribute?

I have a div with id = mainContent. I want jquery to automatically trigger an event when it changes its height. I did something like this:

$("#mainContent").change('height', function() {
    $("#separator").css('height', $("#mainContent").height());
});

I know its wrong.

Here's my whole code (I pasted all of it because I can't get into jsfiddle for some reason I don't know):

$(document).ready(function() {
 $("#separator").css('height', $("body").height());
});

$(function() {
 $("#btnSample1").click(function() {
  $("#mainContent").css('height', '400px');
  $("#mainContent").css('width', '600px');
  $("#mainContent").css('background-color', '#F0F0F0');
 });

 $("#btnSample2").click(function() {
  $("#mainContent").css('height', '1600px');
  $("#mainContent").css('width', '700px');
  $("#mainContent").css('background-color', '#F0F0F0');     
 });

 $("#mainContent").change('height', function() {
  $("#separator").css('height', $("#mainContent").height());
 });
});
html, body {
 width: 100%;
 height: 100%;
 margin: 0;
 padding: 0;
}

#separator {
 border-right: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table style="width: 100%;">
 <tr>
  <td valign="top" style="width: 19%;"> 
   <table id="mainMenu">
    <tr><td><input id="btnSample1" type="button" value="Sample 1"  /></td></tr>
    <tr><td><input id="btnSample2" type="button" value="Sample 2"  /></td></tr>
   </table>
  </td>

  <td valign="top" style="width: 1%;" >
   <div id="separator"></div> 
  </td>

  <td valign="top" style="width: 80%;">
   <div id="mainContent"></div>
  </td>
 </tr>
</table>

I am trying to adjust the height of the div id=separator based on the height of mainContent whenever the height of mainContent changes.

PS: In this case I know I can use the button event to do this but I want the div to trigger the event when the height is changed.

starball
  • 20,030
  • 7
  • 43
  • 238
NinjaBoy
  • 3,715
  • 18
  • 54
  • 69
  • What are you trying to accomplish? – Blender Mar 09 '12 at 03:17
  • I want to automatically adjust the height of separator div based on the height of mainContent div. OK I'll edit my question. – NinjaBoy Mar 09 '12 at 03:20
  • I think you should use CSS for that. Can you post a demo of your problem (a live demo would be good)? – Blender Mar 09 '12 at 03:22
  • 2
    @Blender I want to post it in jsfiddle.net but I can't enter the site today. – NinjaBoy Mar 09 '12 at 03:23
  • Are you asking about when the height of a div is changed by another piece of code, or when it is changed due to the user resizing the browser? (Or both?) – nnnnnn Mar 09 '12 at 03:27
  • @nnnnnn Are you the one who voted this down? BTW, to answer your question I'm asking about when the height of a div is changed by an event. – NinjaBoy Mar 09 '12 at 03:34
  • I saw a similar question before. Maybe you should search through the questions in stackoverflow first. – Derek 朕會功夫 Mar 09 '12 at 03:36
  • Not sure why you picked me (and only me) to ask about the downvote, but no, it wasn't me. (I've _never_ downvoted any question or answer.) What do you mean "changed by an event"? Changed by JS code within an event handler, or changed by the user resizing the browser which triggers a resize event? – nnnnnn Mar 09 '12 at 03:43
  • I don't think polling for height changes is the best solution for this. Try attacking the root of the problem and attach your event functions to the code that changes the element's height. – Blender Mar 09 '12 at 04:11

5 Answers5

63

First, There is no such css-changes event out of the box, but you can create one by your own, as onchange is for :input elements only. not for css changes.

There are two ways to track css changes.

  1. Examine the DOM element for css changes every x time(500 milliseconds in the example).
  2. Trigger an event when you change the element css.
  3. Use the DOMAttrModified mutation event. But it's deprecated, so I'll skip on it.

First way:

var $element = $("#elementId");
var lastHeight = $("#elementId").css('height');
function checkForChanges()
{
    if ($element.css('height') != lastHeight)
    {
        alert('xxx');
        lastHeight = $element.css('height'); 
    }

    setTimeout(checkForChanges, 500);
}

Second way:

$('#mainContent').bind('heightChange', function(){
        alert('xxx');
    });


$("#btnSample1").click(function() {
    $("#mainContent").css('height', '400px');
    $("#mainContent").trigger('heightChange'); //<====
    ...
});    

If you control the css changes, the second option is a lot more elegant and efficient way of doing it.

Documentations:

  • bind: Description: Attach a handler to an event for the elements.
  • trigger: Description: Execute all handlers and behaviors attached to the matched elements for the given event type.
mahatmanich
  • 10,791
  • 5
  • 63
  • 82
gdoron
  • 147,333
  • 58
  • 291
  • 367
54

Please don't use techniques described in other answers here. They are either not working with css3 animations size changes, floating layout changes or changes that don't come from jQuery land. You can use a resize-detector, a event-based approach, that doesn't waste your CPU time.

https://github.com/marcj/css-element-queries

It contains a ResizeSensor class you can use for that purpose.

new ResizeSensor(jQuery('#mainContent'), function(){ 
    console.log('main content dimension changed');
});

Disclaimer: I wrote this library

caesay
  • 16,932
  • 15
  • 95
  • 160
Marc J. Schmidt
  • 8,302
  • 4
  • 34
  • 33
  • Works pretty well! Best solution I've seen yet - not perfect, as it seems to miss some events when multiple resize triggers happen in a short period (like when using a resizing textarea), but still a working solution. – Fateh Khalsa Feb 10 '15 at 02:54
  • 1
    gdoron's answer proposes only 2 options, one being manual click and other being using a timer, which both kinda sucks. I have found this plugin a much better solution, as it is really nice and seamless, as we don't need to capture all dynamic on success events manually, that can cause a height increase. – Tiago Duarte May 21 '15 at 12:35
  • It is a good solution, however, it is quite a big file to include. You have to make the choice of CPU vs page weight here. For me, running a loop function trougth the DOM elements to watch is still a better option . – Gfra54 Jul 03 '15 at 13:00
  • @Gfra54, "big file" what a joke. ResizeSensor.js has only 144 lines of code. ;) – Marc J. Schmidt Jul 03 '15 at 13:44
  • Doesn't works for me. Am I missed something? I included both js files. Added call to constructor, as in example, changed mainContent to id of my div that I sure its height is changed, but callback function never called. No exceptions on console. (The height of my div is changed by angular interpolation. Don't know is it matters. ) – Yuriy N. Nov 11 '15 at 08:10
  • Working like a charm. I needed this for an intranet portal, which includes AJAX- or iFrame-based content from 3rd party applications, pumped into the page after documentReady. I have a sticky header, which has explicit width set and got messed up with scroll bars appearing, if page content increases. Using your lib, I can handle this and easily refresh my header with an event based mechanism. Thanks, Marc! – Windwalker Dec 04 '15 at 11:36
  • @MarcJ.Schmidt, does this work on img loading? For example, I have some JS that resizes some images, but if it runs before images are loaded, the height is zero and the onload event is notoriously unreliable, so after images load it doesnt fire unless I use a timeout or something which is undesirable. If I used this on an image, would it fire on the first load of the image? – Evan Mar 23 '16 at 20:08
  • @Evan, you can't listen on img elements like in the readme stated. You should listen on a div wrapper around the img, than it works. – Marc J. Schmidt Mar 23 '16 at 20:12
  • This utilizes animations. If you don't want to utilize a library, you could handle animations yourself. `['transitionend', 'webkitTransitionEnd', 'oTransitionEnd', 'MSTransitionEnd'].forEach(event => this.el.nativeElement.addEventListener(event, () => this.calculate()));` `['animationend', 'webkitAnimationEnd', 'oAnimationEnd', 'MSAnimationEnd'].forEach(event => this.el.nativeElement.addEventListener(event, () => this.calculate()));` css `div { -moz-transition: 0.5s; -ms-transition: 0.5s; -o-transition: 0.5s; -webkit-transition: 0.5s; transition: 0.5s;}` – TamusJRoyce Aug 06 '20 at 15:42
16

For future sake I'll post this. If you do not need to support < IE11 then you should use MutationObserver.

Here is a link to the caniuse js MutationObserver

Simple usage with powerful results.

    var observer = new MutationObserver(function (mutations) {
        //your action here
    });

    //set up your configuration
    //this will watch to see if you insert or remove any children
    var config = { subtree: true, childList: true };

    //start observing
    observer.observe(elementTarget, config);

When you don't need to observe any longer just disconnect.

    observer.disconnect();

Check out the MDN documentation for more information

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Brandon.Staley
  • 1,742
  • 21
  • 25
  • 1
    To include changes in the for the style attribute you would include `attributes: true` in the `config` object. You could even use `attributeFilter: ['style']` in combination to just pull the style changes. – Brandon.Staley Jun 26 '15 at 11:45
  • That looks really cool. So you are saying that the width and height are part of the style attribute and thus changing them will generate a call to the given callback which can in turn check whether the size changed and maybe trigger another event such as "resized", correct? – Alexis Wilke Jun 26 '15 at 23:12
  • 1
    Yes, you could definitely do that. Though you may want to create you own custom event name like `observedResize` and trigger that. – Brandon.Staley Jun 29 '15 at 13:13
  • It appears using `resize:vertical;` doesn't produce changes. Take a look at it working here with javascript resize http://jsfiddle.net/jfo57rdd/ – Brandon.Staley Apr 27 '16 at 01:56
  • @Brandon.Staley This is not working when the div has image within. In first instance the image is not loaded and div size is set to 0 and after load the div size is increased, but observe method is not triggered – Santhosh Jan 21 '20 at 09:53
3

Another simple example.

For this sample we can use 100x100 DIV-box:

<div id="box" style="width: 100px; height: 100px; border: solid 1px red;">
 // Red box contents here...
</div>

And small jQuery trick:

<script type="text/javascript">
  jQuery("#box").bind("resize", function() {
    alert("Box was resized from 100x100 to 200x200");
  });
  jQuery("#box").width(200).height(200).trigger("resize");
</script>

Steps:

  1. We created DIV block element for resizing operatios
  2. Add simple JavaScript code with:
    • jQuery bind
    • jQuery resizer with trigger action "resize" - trigger is most important thing in my example
  3. After resize you can check the browser alert information

That's all. ;-)

Santhosh
  • 335
  • 1
  • 4
  • 23
Piotr
  • 377
  • 4
  • 11
  • And... sorry for my poor english... I have not enough time for school of english. :-/ – Piotr Feb 27 '13 at 14:07
  • 13
    You trigger resize event yourself? I guess the question was to handle the event when user generates it (increase/decrease browser window size) - not you. – levi Jun 24 '14 at 23:00
3

As far as regards the height or any other dimension parameter, you can use the ResizeObserver interface. First, you get your HTML element:

const divElem = document.querySelector('#mainContent');

The element type is not restricted to DIVs, it can be anything. Then, you create an instance of the ResizeObserver interface:

let myObserver = new ResizeObserver(entries => {
  console.log("Height changed. New height: "+$("#mainContent").height());
});

Finally, you call the observe() method, which starts the specified element:

myObserver.observe(divElem);

Each time the element will be resized, the observe() method will be triggered.

Please note: the ResizeObserver interface does not work with Internet Explorer.

Other valuable answers are here: How to detect DIV's dimension changed?

zwcloud
  • 4,546
  • 3
  • 40
  • 69
Aldo Paradiso
  • 911
  • 2
  • 15
  • 30