Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make widget auto-size height to fit content (no vertical scrollbar) #404

Closed
ashokprajapati opened this issue Mar 28, 2016 · 26 comments · Fixed by #2404, #2405, #2412, #2436 or #2454
Closed

make widget auto-size height to fit content (no vertical scrollbar) #404

ashokprajapati opened this issue Mar 28, 2016 · 26 comments · Fixed by #2404, #2405, #2412, #2436 or #2454

Comments

@ashokprajapati
Copy link

I want height as per inner container, i do not want any scroll bar and i want to put image inside grid-stack-item-content and i want grid-stack-item height same as image Height,

i have tried but i do not get result, can please let me know it;s possible

@somchaisomsi
Copy link

The same issue here, HELP NEEDED!
Set the height of each grid based on their inner contents!

have been pulling my hair for the last 2 days, any reply would be highly appreciated!

@radiolips
Copy link
Member

I believe that what you're asking about can be done with CSS and DOM manipulation. If either of you could provide a jsFiddle, I'd be happy to help achieve the desired outcome.

@ashokprajapati
Copy link
Author

Hi Radiolips

here have more info
i have drag and drop element, every drag have his one html contain inside

please see this url
http://webiberis.com/project/gridstack/demo/

there have 2 div (drag me element ) in this div have image, when i will drag and drop in gridstak element then it's show image inside gridstack container, but in gridstack container have scroll bar and have fixed height

there i need full height same as image height without scrolling.

@ashokprajapati
Copy link
Author

and there i want height as per image height without spacing and scroll bar

@somchaisomsi
Copy link

Hi & thanks for your reply @radiolips,
@ashokprajapati has already provided an example, but here is a jsfiddle example for you:
https://jsfiddle.net/web2fix/3psxL32L/

Please note the section called MIDDLE and sidebars which have scrollbars,
I would like to have the scrollbar removed and update the height of each container (or grid) as much as their inner contents go!

That would be really nice if I could have that on initialization.
I have taken a look at the source and I saw that there is an "auto" option for cellHeight so it would be nice to be able to pass a variable like "dynamic" which could read the $(content's).scrollHeight for each grid and set it as cellHeight for that (each) grid.

isAutoCellHeight = this.opts.cellHeight === 'auto'; isDynamicCellHeight = this.opts.cellHeight === 'dynamic'; if (isAutoCellHeight) { self.cellHeight(self.cellWidth(), true); } else if (isDynamicCellHeight) { // then get the height of each inner container and calculate it as a grid height or in this case cellHeight } else { this.cellHeight(this.opts.cellHeight, true); } ;

@somchaisomsi
Copy link

I tried to fix this issue temporarily but something is not right with the cellHeight
I set the cellHeight to be 1 pixell then calculated the inner height and passed to the divs as data-gs-height="calculated inner height"

then this happened: the columns are now extremely long! even some of them do not have any contents.
some columns are supposed to have just 100px height but they are going insanely longer than 100px
extremely in need of your help @radiolips!
example after the dirty fix which brings up the cellHeight bug: jsfiddle

@somchaisomsi
Copy link

Help still needed! I am still pulling my hair! has been a week. Let me know if this can not be done with grid stack. Just saying that "It can be done with CSS and DOM manipulation" will not solve our issue!

@ashokprajapati
Copy link
Author

i also want answer for this question.

@webdesignmzm
Copy link

I believe this is one of the most important features that should be included! I have the same issue working around is not as easy as @radiolips says!
I have been working around for days and no results! css manipulation does not work and javascript height recalculation after initialization also slows down the loading time!
The only one way to fix this is on initialization through the plugin itself.
if you think otherwise then prove it @radiolips!

@fredagsfys
Copy link

Is there a solution to this yet? @radiolips @webdesignmzm

@MarkH170
Copy link

Also looking for a solution to this, any updates?

@preetsin
Copy link

I am looking for solution as well.

@radiolips
Copy link
Member

Apologies for the delay.

Firstly, @webdesignmzm and @somchaisomsi, I'd like to point out that this library, and the support everyone receives, is entirely free of charge. Please be more considerate to the contributors who are spending their own time to provide a better dashboard experience for all users.

I am closing this ticket. Before closing it, I will say (note that I am now now, nor have I anywhere on this ticket, said the word "easy") that this can be done using Javascript and CSS. This Stack Overflow article (http://stackoverflow.com/questions/2522579/how-do-i-get-the-real-height-of-a-overflow-hidden-or-overflow-scroll-div) explains how to grab the height of a div with a scrollbar. If you put this code into a setTimeout or if you run it onchange from gridstack, you'll have widgets that have height that auto-update based on content. Width may be adjusted the same way. I have built this code based on @ashokprajapati 's link.

$('.grid-stack').data('gridstack').resize(
    $('.grid-stack-item')[5],
    $($('.grid-stack-item')[5]).attr('data-gs-width'),
    Math.ceil(($('.grid-stack-item-content')[5].scrollHeight + $('.grid-stack').data('gridstack').opts.verticalMargin) / ($('.grid-stack').data('gridstack').cellHeight() + $('.grid-stack').data('gridstack').opts.verticalMargin))
);

This code simply calls resize on a widget, doesn't change its width, and updates its height to be the correct math to determine height (this assumes verticalMargin is in pixels).

@SimonOrdo
Copy link

SimonOrdo commented Sep 8, 2016

FYI for people coming in later, I've found that it works a little better if you use round instead of ceil.


        var grid = $('.grid-stack').data('gridstack');
        var gsi = $(elemId).parents(".grid-stack-item");

        var newHeight = Math.round((gsi[0].scrollHeight + grid.opts.verticalMargin) / (grid.cellHeight() + grid.opts.verticalMargin));

        grid.resize(gsi,$(gsi).attr('data-gs-width'),newHeight);

@radiolips
Copy link
Member

@SimonOrdo Could you explain why using Math.round would be better? If the content is even one pixel over x height, height should be set to x+1 - thus, Math.ceil is the recommended option.

@webdesignmzm
Copy link

webdesignmzm commented Sep 9, 2016

@SimonOrdo
Hi Thanks, would you please care to update the following example from @somchaisomsi with your code? I can't get it to work!
NEW LINK: https://jsfiddle.net/W2F/4yqjux2c/

Would you please send us the updated js fiddle link?

@kreintjes
Copy link

@SimonOrdo @radiolips at first I thought Math.round indeed works better. With Math.ceil the item always becomes a bit larger than expected when doing a manual resize. At first I couldn't find any situation where the item is not large enough when using round. However, after some more testing I ran into some issues when using round instead of ceil where the height wouldn't be large enough, thus resulting in a vertical scrollbar, which I would like to prevent. Using ceil fixes this problem, but causes some other issues:

  1. For new widgets the calculated becomes a bit too large sometimes. When I reduce the height manually, I can make it smaller, while still not getting a scrollbar.
  2. When resizing manually (dragging and dropping the vertical resize handle) the height gets always rounded up (due to the ceil). This is confusing, because the drop shadow (dashed line) implies the height would be reduced (it is smaller than the current widget height). However when releasing the mouse button, the widget grows nevertheless. Only when dragging the widget to be smaller (above) the drop line can the widget's height be decreased. See screenshot:

image

  1. Also sometimes (in Firefox) a vertical scrollbar appears when resizing an element manually. This makes the widget slightly smaller, increasing the content's height and thus increasing the calculated widget height. After releasing the mouse, the scrollbar disappears, the widget becomes broader, the content's height decreases and the widget ends up being far too large. But this is easily fixable with an overflow-y: hidden on the grid-stack-item-content.

Any help with the other problems would be greatly appreciated!

@mousumipaul
Copy link

I am trying to check that gridstack panel takes height based on its inner container height. I was using the code of responsive.html file which I downloaded from gridstack.
I just put some new line in the div to see if it is expanding or not. My changed code is:

<div class="content-wrapper">
    <div class="grid-stack">

    </div>
</div>

<script type="text/javascript" src="../asset/chroma.js-master/chroma.js"></script>

<script type="text/javascript">
    $(function () {
        // thanks to http://stackoverflow.com/a/22885503
        var waitForFinalEvent=function(){var b={};return function(c,d,a){a||(a="I am a banana!");b[a]&&clearTimeout(b[a]);b[a]=setTimeout(c,d)}}();
        var fullDateString = new Date();
        function isBreakpoint(alias) {
            return $('.device-' + alias).is(':visible');
        }
        var options = {
            float: false
        };
        $('.grid-stack').gridstack(options);
        function resizeGrid() {
                var grid = $('.grid-stack').data('gridstack');
                if (isBreakpoint('xs')) {
                    $('#grid-size').text('One column mode');
                } else if (isBreakpoint('sm')) {
                    grid.setGridWidth(3);
                    $('#grid-size').text(3);
                } else if (isBreakpoint('md')) {
                    grid.setGridWidth(6);
                    $('#grid-size').text(6);
                } else if (isBreakpoint('lg')) {
                    grid.setGridWidth(12);
                    $('#grid-size').text(12);
                }
            };
            $(window).resize(function () {
                waitForFinalEvent(function() {
                    resizeGrid();
                }, 300, fullDateString.getTime());
            });
            new function () {
                this.serializedData = [
                    {x: 0, y: 0, width: 4, height: 4},
                    {x: 4, y: 0, width: 4, height: 4},
                    {x: 8, y: 0, width: 4, height: 4},
                    {x: 0, y: 4, width: 6, height: 4},
                    {x: 6, y: 4, width: 6, height: 4}
                ];
                this.grid = $('.grid-stack').data('gridstack');
                this.loadGrid = function () {
                    this.grid.removeAll();
                    var items = GridStackUI.Utils.sort(this.serializedData);
                    _.each(items, function (node, i) {
                        this.grid.addWidget($(layout("pie", node.id)),
                            node.x, node.y, node.width, node.height);
                    }, this);
                    return false;
                }.bind(this);
                this.loadGrid();
                resizeGrid();
        };
    });
</script>

But the div is not at all expanding. I did not understand how I will use the solution provided by @radiolips . Where shall I change the code.

@jasondalycan
Copy link

The following is an alternate solution to dynamically size a grid cell based on its content using the grid's update function. Here widgetInstance is a reference to the widget object and grid is the grid object:

let contentElement = widgetInstance.el.querySelector(".grid-stack-item-content").children[0].children[0];
let contentHeight = Math.ceil((contentElement .parentElement.clientHeight * 1.05) / grid.opts.cellHeight);  // Get height in rows.  Multiply by 1.05 for some buffer

contentElement.parentElement.parentElement.style.overflow = "hidden";  // Hide scrollbars if desired
grid.update(widgetInstance.el, {h: contentHeight});

@adumesny
Copy link
Member

adumesny commented Mar 18, 2023

I think it's time to re-open this and do it the right way if someone is willing to donate or contribute a review. some sort of contentHeight option on the grid/gridItems....

@jasondalycan Math.ceil() ought to be enough without 5% fuge (whcih can be a lot), but you are missing is taking the padding off (default is 10px on each side) before finding a fit.

Also your element is specific to your use case of course. widgetInstance.el.querySelector(".grid-stack-item-content") should be enought if you don't overflow:hidden.

but you are right my new update() method will do once you find that row number, until somethng resizes and you get notified (that the tricky part and when the DOM has the wanted size loaded)

@adumesny adumesny reopened this Mar 18, 2023
@ensemblebd
Copy link

ensemblebd commented Mar 18, 2023

Not sure if this is helpful, but figured I'd share.
Forgot about this issue years ago and saw the email about reopening.
Been using the following code for years and works well for my use-case.

In retrospect while typing this up, I'd probably put this in the "conceptual" category of things.. @jasondalycan has a cleaner solution, that is.
And in that the only mathematical value added here is the usage of scrollheight, and maybe factoring hard horizontal padding (which stretches the item contents vertically the smaller it gets, like a margin).
Fundamentally it's just a window resize event adjustment, and it's not perfect. I think the "threshold" part is wonky and needs improved. Majority of the time, it gets it right though without overflow.

jQuery.fn.getElementAspects = function () {
	return {
		border: [
			$(this).outerWidth() - $(this).innerWidth(),
			$(this).outerHeight() - $(this).innerHeight()
		],
		padding: [
			$(this).innerWidth() - $(this).width(),
			$(this).innerHeight() - $(this).height()
		],
		margin: [
			$(this).outerWidth(true) - $(this).outerWidth(),
			$(this).outerHeight(true) - $(this).outerHeight()
		]
	};
};

(function ($, sr) {
	// debouncing function from John Hann
	// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
	var debounce = function (func, threshold, execAsap) {
		var timeout;

		return function debounced() {
			var obj = this, args = arguments;
			function delayed() {
				if (!execAsap)
					func.apply(obj, args);
				timeout = null;
			};

			if (timeout)
				clearTimeout(timeout);
			else if (execAsap)
				func.apply(obj, args);

			timeout = setTimeout(delayed, threshold || 100);
		};
	}
	// smartresize
	jQuery.fn[sr] = function (fn) { return fn ? this.bind('resize orientationchange', debounce(fn)) : this.trigger(sr); };
})(jQuery, 'smartresize');



$(document).ready(function() { 
   var options = {
      cellHeight: 'auto', // unsure if necessary or even impactful. but this is how i have it.
   };
   var grid =  GridStack.init(options, $('#my-grid') );;

   // initial refresh. Depending on your load style, you may want to drop this into a setTimeout.
   handleGridResize(grid);
   
   // and when window resizes:
   $(window).smartresize(function () {
      handleGridResize(grid);
   });

 });



// resize the grid based on inner contents. Should be called when content changes, or when page loads or resizes.
function handleGridResize(grid) {
	var targets = $('.grid-stack-item:not(.grid-stack-placeholder)');
	if (targets.length < 1) return;

	var CELL_HEIGHT = grid.cellHeight();
	targets.each(function () {
		var the_content = $(this).find('.grid-stack-item-content .gsi-content');
		var el = (the_content[0]);

		var el_height = el.scrollHeight; 
		var diff = el_height % CELL_HEIGHT;  // remainder between real height and the gridstack fed calculated height. ("how far off are we from reality")

		// keep in mind that these are GRID size values, not pixel. 1,2,4,6,8,10,12.
		var height = Math.floor(el_height / CELL_HEIGHT); // difference between actual REAL height & gridstack's
		var width = $(this).attr('data-gs-width'); // user preference, direct feed. Default value passed to the resize func below.

		var threshold = the_content.parent().getElementAspects().padding[0];
		
		// when the difference between actual content height differs from the decided-height by gridstack..  (ie. overflow detected)
		// and that difference is greater than the horizontal padding on both sides(2x) + some buffer tolerance (thus 3x) (to increase likelihood of occurrence).. 
		// we should then set the height to the next-highest grid size
		if (diff>0 && diff!=el_height && diff > threshold/3) {
			height = Math.ceil( el_height / CELL_HEIGHT );
		}

		grid.resize(
			$(this)[0],
			width,
			height
		);
	});
}

@adumesny adumesny changed the title Update Height as per inner contain or grid-stack-item-content make widget auto-size height to fit content (no vertical scrollbar) May 6, 2023
@adumesny
Copy link
Member

adumesny commented Jun 13, 2023

this is very application specific as how tall a widget needs to be is highly depends on your specific content layout/DOM heirarchy. Here is code I just added in my Angular app for reference if it helps someone (comment to map to GS classes). I call this whenever content changes, or grid resizes.

I don't think I can move this into the generic lib I'm afraid, as it assumes content takes available space, while first and only child is sized for content and would clip in my case.

  /** Call to make the gridItem match the content height of the finished loaded widget */
  public resizeToContent() {
    const el = this.el; // .grid-stack-item
    if (!el) return;
    let height = el.getBoundingClientRect().height;
    const card = el.querySelector('.card-block'); // .grid-stack-item-content that fills remaining space between header+footer
    if (!card) return;
    const cardH = card.getBoundingClientRect().height;
    const contentH = (card.firstChild as Element)?.getBoundingClientRect().height || cardH; // actual widget inside that may clip out or have extra space
    height += contentH - cardH;
    const grid = this.grid.grid; // GridStack
    const cell = grid?.getCellHeight();
    if (!cell) return;
    let h = Math.ceil(height / cell);
    const opt = this.options; // GridStackWidget
    if (opt.maxH && h > opt.maxH) h = opt.maxH;
    if (h !== opt.h) grid.update(el, {h});
  }

Note: this bypass the needs to know the padding/margin as it just figure out how big it is vs how much bigger to fit the content unclipped and size the outside accordingly.

@adumesny adumesny reopened this Jun 13, 2023
adumesny added a commit to adumesny/gridstack.js that referenced this issue Aug 7, 2023
* fix gridstack#404
* added `GridStackOptions.fitToContent` and `GridStackWidget.fitToContent` to make gridItems size themselves to their content (no scroll bar), calling `GridStack.resizeToContent(el)` whenever the grid or item is resized
* added demo showing behavior
* fixed sizing event to use much more accurate ResizeObserver on grid rather than generic window.addEventListener('resize')
adumesny added a commit to adumesny/gridstack.js that referenced this issue Aug 7, 2023
* fix gridstack#404
* make sure to use batch mode to improve re-layout
@adumesny
Copy link
Member

adumesny commented Aug 7, 2023

fixed in the next release v9! even though items have height=1 to start with they are sized to content and reflow as needed...

long overdue feature - don't forget to donate to help support this lib!

20230807_093423.mp4

@PGabriel20
Copy link

PGabriel20 commented Aug 10, 2023

@adumesny is this feature already available to use in production? i just updated to 8.4.0 right now but still cant see the fitToContent option

@adumesny
Copy link
Member

as I mentioned it will be in v9 - still need to consume it in my app and test it some more...

adumesny added a commit to adumesny/gridstack.js that referenced this issue Aug 23, 2023
* changing column size and manually resizing a widget will now call resizeToContent()
* added resizeToContentCB for app to override how to resize.
* Added `resizeToContentParent` to make generic code more robust.
* fixed some 1 column loading Nan issues
adumesny added a commit to adumesny/gridstack.js that referenced this issue Aug 30, 2023
* `sizeToContent` now supports being `boolean|number` to limit the height but user can resize past that, unlike maxH.
* more gridstack#404 fixes
adumesny added a commit to adumesny/gridstack.js that referenced this issue Sep 4, 2023
* more fix gridstack#404
* resizeToContentParent is now GridStackWidget var as well (local override) for widgets that needs to resize differently.
adumesny added a commit to adumesny/gridstack.js that referenced this issue Sep 6, 2023
* fix resizeToContent() to handle node.h (using when cellHeight changes or we resize) vs DOM sizing (rest of the time)
more gridstack#404
@adumesny
Copy link
Member

works for nested grid too!

20230910_120205.1.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment