Suppose we need to set the autoheight of an ag-grid component it can be done easily with setting gridOptions to domLayout="autoHeight". This works in a single component, but for master-detail (parent/children) component that heights can be expanded, this doesn't work.
Same issue :
https://github.com/ag-grid/ag-grid/issues/205
I need to tweak deep into its css but still can't make it work,
Style reference: https://www.ag-grid.com/javascript-grid-styling/
Ag grid DOM Layout: https://www.ag-grid.com/javascript-grid-width-and-height/#gsc.tab=0
Example to reproduce :
https://github.com/ag-grid/ag-grid-vue-example (see on Master/Detail)
It's either tweaking the gridOptions getRowheight or its embedded css
for the related css :
.ag-root {
/* set to relative, so absolute popups appear relative to this */
position: relative;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
/* was getting some 'should be there' scrolls, this sorts it out */
overflow: hidden;
}
.ag-body {
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
https://github.com/ag-grid/ag-grid/blob/master/dist/styles/ag-grid.css
and a plunker inside:
https://www.ag-grid.com/javascript-grid-master-detail/#gsc.tab=0
Another clue from the author: https://www.ag-grid.com/javascript-grid-row-height/index.php#gsc.tab=0
Height for Pinned Rows Row height for pinned rows works exactly as per
normal rows with one difference - it is not possible to dynamically
change the height once set. However, this is easily got around by just
setting the pinned row data again which resets the row heights.
Setting the data again is not a problem for pinned rows as it doesn't
impact scroll position, filtering, sorting or group open/closed> positions as it would with normal rows if the data was reset.
You can dynamically calculate row height.
getRowHeight: function (params) {
if (params.node && params.node.detail) {
var offset = 80;
var allDetailRowHeight = params.data.callRecords.length * 28;
return allDetailRowHeight + offset;
} else {
// otherwise return fixed master row height
return 60;
}
}
You can find this example in official documentation of ag-grid.
For anyone finding this question in late 2021 and beyond, there's now the detailRowAutoHeight property in the gridOptions which will achieve the results desired by the asker.
https://www.ag-grid.com/javascript-data-grid/master-detail-height/#auto-height
const gridOptions = {
// dynamically set row height for all detail grids
detailRowAutoHeight: true,
// other grid options ...
}
Related
I attached an image to can exemplify the problem. I have 4 elements in page:
a table (which is an header with some data)
a chart
a table under the chart (which use chart labels as table header)
a footer
Because first table have only 2 rows (and is static), tableChart also is static (with only 5 rows) and the footer have only one row, I want to calculate dynamically the height of the chart.
So, I get window.height (or container) and decrease table height, tableChart height and footer height. Then, assign the value to chart element. I do this inside AfterViewInit lifecycle (I tried also DoCheck), but can't solve a problem: because #chart element is rendering in same time with the rest, when I get #chartTable height inside setChartHeight() method, it get only a part of total height necessary to render all 5 rows, so the chart will have a bigger height than the necessary.
To be more specific:
chartTable have an normal height of 100px;
because the setChartHeight() is called too early, chartTable is found having only 20px (I think can render only first row)
so, from the total of 500px (example) will decrease only 20px, instead of 100px and the chart height will be 480px (instead of 400px)
How can I wait to render all elements out of chart, and just at the final, to calculate the height of it in a right mode.
ngAfterViewInit(): void {
this.setChartHeight();
}
setChartHeight(): void {
let container = document.getElementById('container');
if (typeof container == 'undefined' || container == null) {
return;
}
let table = document.getElementById('table');
let chartTable = document.getElementById('chartTable');
let footer = document.getElementById('footer');
const height = container.clientHeight - table.clientHeight - chartTable.clientHeight -
footer.clientHeight;
let chart = document.getElementById('chart');
chart.style.height = height + 'px';
}
Another problem which I think exists is that chart and chartTable have same data source. So, when the source will came, because the chart element is above chartTable element, it will be rendered first. So, I need to render chartonly when I'm sure thatchartTable` is already displayed on the page.
edit: codesandbox example
thanks
Finally, how Strella and Liam said, I found a solution using css and avoiding reuse flex-layout library.
.container {
height: 100%;
display: flex;
flex-flow: column;
}
.table {
flex: 0 1 auto;
}
.chart-container {
flex: 1 1 auto;
display: flex;
flex-flow: column;
&__chart {
flex: 1 1 auto;
}
&__table {
flex: 0 1 100px;
}
}
The IntersectionObserver is triggered when an element is visible in the viewport for a certain amount (0-100%). This means, when the element is already 100% in the viewport it does not trigger anymore, as there is no change on the threshold.
I have a element that has a height of 200vh and I want the IntersectionObserver to trigger, when I scroll over this element. So the element is always 100% inside the viewport.
Is there a way to trigger the observer while scrolling over the element?
I cannot use the scroll event, as I am using a CSS scroll-snap, which causes the event to be swallowed by the browser, before JS can detect it.
Hopefully I was able to grasp your challenge here, so I'll attempt to propose a solution that should work for your use case, even though there's no code to use as a reference.
From my understanding you're using scroll-snap to snap sections as the user interacts by doing scroll and your intention is to have the Intersection Observer to trigger as the users move from section to section.
In the following example you'll see how sections are being snapped but the debugger shows which section is being shown to the user.
const debuggerSpan = document.querySelector('#current-section');
const sections = [...document.querySelectorAll('.scroll-snap-item')];
const div = document.querySelector('.scroll-snap-container');
/*
* This method will get called any time a section touches the top
* of the viewport.
*/
const intersectionDetected = (entries, observer) => {
entries.forEach((entry) => {
const {
innerText
} = entry.target;
if (!entry.isIntersecting) return;
// Making it obvious that the current section is correct.
debuggerSpan.innerText = innerText;
});
};
const observer = new IntersectionObserver(intersectionDetected, {
/*
* Root should be div and not the default (doc).
*/
root: div,
/*
* Negative margin from the bottom creates an invisible line
* to detect intersections.
*
* The reason why the recommendation is to use -1% and -99% is to
* avoid the race condition between two intersections happening
* (AKA the section about to be out of the viewport and the section
* about to enter the viewport).
*/
rootMargin: '-1% 0% -99% 0%',
/*
* Default value but making it explicit as this is the
* only configuration that works.
*/
threshold: 0
});
sections.forEach(section => observer.observe(section));
.scroll-snap-item {
height: 100vh;
display: grid;
place-items: center;
font-size: 4rem;
scroll-snap-align: start;
}
.scroll-snap-container {
scroll-snap-type: y mandatory;
overflow-y: scroll;
height: 100vh;
}
/* Decorative stuff below*/
.scroll-snap-item:nth-child(odd) {
background-color: gray;
}
aside {
position: fixed;
font-size: 1.6rem;
bottom: 16px;
right: 16px;
background-color: #333;
color: white;
padding: 16px;
border-radius: 32px;
}
<div class="scroll-snap-container">
<section class="scroll-snap-item">1</section>
<section class="scroll-snap-item">2</section>
<section class="scroll-snap-item">3</section>
<section class="scroll-snap-item">4</section>
<section class="scroll-snap-item">5</section>
<section class="scroll-snap-item">6</section>
</div>
<aside>Current section: <span id="current-section"></span></aside>
I wrote a couple of practical posts that cover what's behind all this and what was the thought process to address this situation. Please feel free to give it a read and leave a comment if things are not clear enough:
Scrollspying made easy with the Intersection Observer API.
A graphical introduction to the Intersection Observer API.
Both are quick reads and should provide everything you need to tackle this and even more complex problems with the Intersection Observer. Also, feel free to play around with this tool I wrote called The Intersection Observer Playground where you can try out different configurations and see how they affect the intersection triggers.
Hope this is helpful!
I'm working on an HTML5 browser game that can be divided into 3 parts: two UI panels on the left and right of a center set of square canvases for the playing surface. The three panels need to be horizontally aligned, and the total game needs to keep an aspect ratio of 16:9. The left and right panels should be of equal widths, and all three panels must be of equal height. I have specified a minimum width and height inside a resize() function called when an onresize event is detected.
Currently, each panel is a div, and all three are contained inside a section. Right now, the section isn't necessary, but I want to keep the game separated from extra content at the bottom of the screen that I might choose to add later.
The CSS style is as follows:
* {
vertical-align: baseline;
font-weight: inherit;
font-family: inherit;
font-style: inherit;
font-size: 100%;
border: 0 none;
outline: 0;
padding: 0;
margin: 0;
}
#gameSection {
white-space: nowrap;
overflow-x: hide;
overflow-y: hide;
}
#leftPanel, #centerPanel, #rightPanel {
display: inline-block;
}
#leftPanel {
background-color: #6495ed;
}
#centerPanel {
background-color: #e0ffff;
}
#rightPanel {
background-color: #b0c4de;
Right now, I have set the background color of each div just to show me when I'm correctly setting the size of each div.
The body of my HTML document is as follows:
<body onresize="resize()">
<section id="gameSection">
<div id="leftPanel">Left Panel.</div>
<div id="centerPanel">Center Panel.</div>
<div id="rightPanel">Right Panel.</div>
</section>
</body>
And finally, my resize() function (I created a separate function for resizing the game in case I add more elements below later):
function resize() {
var MIN_GAME_WIDTH = 800;
var MIN_GAME_HEIGHT = 450;
var GAME_ASPECT_RATIO = 16 / 9;
var width = window.innerWidth;
var height = window.innerHeight;
var gWidth, gHeight;
if(width < MIN_GAME_WIDTH || height < MIN_GAME_HEIGHT) {
gWidth = MIN_GAME_WIDTH;
gHeight = MIN_GAME_HEIGHT;
}
else if ((width / height) > GAME_ASPECT_RATIO) {
<!-- width is too large for height -->
gHeight = height;
gWidth = height * GAME_ASPECT_RATIO;
}
else {
<!-- height is too large for width -->
gWidth = width;
gHeight = width / GAME_ASPECT_RATIO;
}
resizeGame(gWidth, gHeight, GAME_ASPECT_RATIO);
}
function resizeGame(var gWidth, var gHeight, var aspectRatio) {
var gSection = document.getElementById("gameSection");
var lPanel = document.getElementById("leftPanel");
var cPanel = document.getElementById("centerPanel");
var rPanel = document.getElementById("rightPanel");
gSection.height = gHeight;
gSection.width = gWidth;
<!-- should the below be taken care of in the CSS? -->
lPanel.height = gHeight;
cPanel.height = gHeight;
rPanel.height = gHeight;
cPanel.width = cPanel.height;
lPanel.width = (gWidth - cPanel.width) / 2;
rPanel.width = lPanel.width;
}
I've tried a number of different commands to resize the divs, but it just isn't working for me. When I try adding test canvases, color appears, but the boxes still aren't the correct size. I have also considered loading an invisible background image to each div and scaling it to the desired size; however, I was able to resize my canvas using the above method before and it seemed to work just fine.
Additional Notes
While I've already had pretty good success resizing a single canvas, I don't want to use just one canvas for the game because not all parts of the UI need to be drawn at the same time.
I'm trying to keep this solely in Javascript.
I suspect that I could just use CSS to handle resizing by fixing the aspect ratio to 16:9 and using width:56.25% for the center panel and width:21.875% for the side panels, but that limits me to one aspect ratio and doesn't explain why my above script isn't working.
I can provide the entire HTML file if needed. This is what it's supposed to look like:
End Goal (without right panel)
Thank you!
UDPATE:
jsfiddle
I got it kind of working here. I made a lot of changes/minor fixes to the code before finding what was wrong (other than various syntax errors):
You were using .width and .height instead of .style.width and .style.height, and you were applying integers to these instead of strings with "px" appended to them. Both of these things are completely understandable to miss.
I also moved the onresize from the body tag into the JS, don't know why it wasn't working on jsfiddle, but this is good practice anyways.
In the future: learn how to debug JS using the console and when you ask questions, use small examples, not your entire codebase. This question could have been simplified to "How do I resize a div?" with one line of JS and one div. You also should consider not doing this specific thing in JS, and using flexbox as redbmk said.
I have some javascript that applies a fixed class to my sidebar, so when you scroll, the menu stays with you. Stackoverflow has this with the similar questions sidebar.
$(function() {
var top = $('.side-menu').offset().top - parseFloat($('.side-menu').css('margin-top').replace(/auto/, 0));
$(window).scroll(function (event) {
// what the y position of the scroll is
var y = $(this).scrollTop();
// whether that's below the form
if (y >= top) {
// if so, ad the fixed class
$('.side-menu').addClass('fixed');
$('body').addClass('fixed-sidebar');
} else {
// otherwise remove it
$('.side-menu').removeClass('fixed');
$('body').removeClass('fixed-sidebar');
}
});
});
In my CSS I have * { box-sizing: border-box; } which is causing the else to fire off and the page jumps. When I removed the box-sizing, the fixed menu works as desired.
My question is
Is there another way to achieve what i'm trying to do?
Is there a way to unset the box-sizing property?
EDIT
User this link for a demo: Resize the browser window at various heights and you'll see the issue. http://dev.danielcgold.com/fixed-menu.html
You can always do (selector) { box-sizing: content-box } of course. (That will "unset" your box-sizing...)
I'm using the YUI Panel element that is rendered via an AJAX call. Is there a way to set a maximum height and use a scrollbar whenever the content of the panel exceeds that height?
Definitely! Here is an example with a Dialog with id dlg:
var dialog = new YAHOO.widget.Dialog("dlg", { fixedcenter : true....
So then set the bd css class to auto overflow:
#dlg .bd {
overflow: auto;
}
And then in your js put something like:
YAHOO.util.Dom.setStyle(dialog.body, "height", "300px");
You'll probably need to add some styling to the ft class also, but that's up to you:
#dlg .ft {
padding-top: 10px;
border-top: 1px solid #CECECE;
}
And if you don't care about a specific height, but just want to avoid it being larger than the window then the following code handles that.
myDialog.renderEvent.subscribe(function() {
// Find .yui-panel .bd
let dialogElement = $('.yui-panel .bd');
// set the max-height to the 90% of the screen
let windowHeight = $(window).height();
dialogElement.css('max-height', windowHeight*0.9);
// set overflow:auto
dialogElement.css('overflow', 'auto');
});