I wrote a small site that queries a MongoDB instance containing prior brute-force attempts by IP to help with firewall management. Originally written with a table layout, I rewrote the site to use a grid layout. The page layout and grid CSS are as follows:
The grid-template uses alternative named areas:
/* using alternative named grid areas */
.gridlayout {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: 50px auto auto 50px;
gap: 4px;
grid-template-areas:
"header header"
"left right"
"data data"
"footer footer";
}
header {
grid-area: header;
/* padding: 15px 15px 10px 15px; */
}
left {
grid-area: left;
/* overflow-y: scroll; */
min-height: 0;
min-width: 0;
}
right {
grid-area: right;
min-height: 0;
min-width: 0;
}
data {
grid-area: data;
}
footer {
grid-area: footer;
margin-top: 10px;
}
header, footer {
background-color: #22243E;
/* text-align: center; */
}
The page-controls are in the left grid area and the grid area also shows/hides divs with the HTML forms, snippets of information like site config (path variables/content), a dump of the PHP PRG $_SESSION['xxx'] values, and database connection status, etc.. The right grid area contains up to the 10 most recent database queries. The data section shows any response database records in table format. This all worked well without any recalculation of the grid, showing/hiding information in the left grid area and updating the values in the right grid area and data area as needed.
For example, a quick screenshot of the page is:
Showing the Database status simply expands within the left grid area (as do all of the other divs controlled by the buttons shown):
The "Config", "PRG", and "Database" buttons all show/hide divs in that left grid area and none have ever caused reflow until the "CIDR" button was added to include another 3-input form that uses the same area as the Quick Query and full Query forms do (they are all mutually exclusive and only 1 form is actually shown in the page at any one time) That all works perfectly fine.
The page source and div layout is:
<?php
declare(strict_types=1); /* require strict type declarations */
/* set pageTitle */
$pageTitle = 'journal COMM of BadActors';
/* require ./include/site.php which has php preamble for each page */
require_once __DIR__ . '/include/site.php';
/* PRG must be before header
* handle POST submission and confirmation
*/
include './include/site-PRG.php';
/* include page header info */
include 'header.php';
?>
<div class="gridlayout">
<header> <!-- grid header div -->
<div id = 'header'>
<p id = 'hdrtext' class = 'hftext'>Journal Bad-Actors - in Grid</p>
</div>
</header> <!-- grid header div close -->
<left> <!-- grid left div -->
<div style=''>
<div class = 'container' style = 'margin-left: 15px'>
<p class = 'mp0'>
<span style='font-size: 110%; font-style: italic;'>
<b>Prior Brute-force Attempts</b>
</span><br>
<i>Network Security Class is in...</i><br>
<hr>
<button type='button' onclick='toggledisp("showconfig")'>Config</button>
<button type='button' onclick='toggledisp("showprg")'>PRG</button>
<div id = 'showconfig' class = 'container' style='display: none;'>
<?php $config->showcfg(); ?>
<b>incldir():</b> <?php echo $config->get_siteincl(); ?> <br>
<b>stylesheet:</b> <?php echo $config->get_sitecss() . '/site.css'; ?> <br>
</div>
<div id = 'showprg' class = 'container' style='display: none;'>
<?php showprg(); ?>
</div>
</p>
<p class = 'mp0'>
<hr>
<button type='button' onclick='toggledisp("dbmongo")'>Database</button>
<button type='button' onclick='toggledisp("qweryresults")'>Records</button>
<button type='button' onclick='querydisp()'>Query</button>
<!-- <button type='button' onclick='toggledisp("frmcidr")'>IP/CIDR</button>
<button type='button'
onclick="window.location='https://2pi.3111skyline.com/tmp/db/cidr/';">
IP/CIDR
</button> -->
<button type='button' onclick='cidrdisp()'>IP/CIDR</button>
<!-- <button type='button' onclick='toggledisp("frmipqwry")'>Query</button> -->
<!-- for query result count, just use DOM table.rows.length - 1 instead of
converting from the mongo PHP cursor object, must simpler -->
<div id = 'qrcount' style = 'float: right; background: #32313B; font-size: 10pt; padding: 5px; margin-top: 3px;'></div>
<div id = 'nstfrmblk' style = 'overflow: scroll; min-height: 0; min-width: 0;'>
<div id = 'qdiv' style='display: block;'>
<?php include './include/frm-query-rhost-ruser.php'; ?>
<?php include './include/frm-query-quick-rhost.php'; ?>
</div>
<!-- <div id = 'cdiv' style='display: none; overflow: scroll;'> -->
<?php include './include/frm-cidr.php'; ?>
<!-- </div> -->
</div>
<div id = 'dbmongo' class = 'container' style='display: none;'>
<?php
$dbobj->get_db_status();
if (isset ($_SESSION['insstr'])) {
printf ("document: %s<br>", $_SESSION['insstr']);
unset ($_SESSION['insstr']);
}
?>
</div>
</p>
<!-- <p class = 'mp0'> -->
<?php
/* output any errors thrown */
if ($dmsg->msg_count() > 0) {
echo "<p class = 'mp0'>errors: " . $dmsg->err_count() . ", warnings: " . $dmsg->warn_count() . ", messages: " . $dmsg->info_count() . "<br>";
$dmsg->errmsg (7);
echo "</p>";
}
?>
<!-- </p> -->
</div>
</div>
</left> <!-- grid left div close -->
<right class='container' style='margin-right: 20px;'> <!-- grid right div -->
<!-- query history displays last 10 DB queries -->
<div id = 'qhistdiv'>
<i><b>
Query History List
</b></i>
<div id='qryhist' class='container' style='overflow-y: scroll;'></div>
</div>
<!-- CIDR/IP range result display (toggles qhistdiv display: 'none') -->
<div id = 'cidrdiv' style = 'display: none;'>
<div id = 'rttitle' style='font-weight: bold; font-style: italic;'></div>
<div id = 'cidrres' class = 'container'>
<div id = 'cidrdesc' style='background: #1D1E23; font-weight: bold; margin-bottom: 3px; padding: 5px 0 5px 5px;'></div>
<div id = 'cidrdata' style='padding-left: 15px;'></div>
</div>
</div>
</right> <!-- grid right div close -->
<data id = 'qweryresults'
class = 'container'
style = 'margin: 10px 20px 0 15px; display: none;'>
<!-- grid data div -->
<div id = 'qresultstable'>
<?php
if (isset ($_SESSION['queryready'])) {
$dbobj->db_query_fmt ($_SESSION['qobj'], 'fmt_journal_id');
// unset ($_SESSION['queryready']);
}
else {
/* just output table headings so nrows is 0 below and Query Results is 0 */
printf ("<div class = 'container'><table id='queryres' class='rowalt' style='width: 80%%; margin: 0 auto;'><caption style='font-size: 110%%; margin-bottom: 10px;'><b>Badactors - rhost, ruser<b></caption><tr style='background-color: #32313B; font-weight: bold;'><th style='width: 25%%; padding: 5px 0 5px 5px;'>timestamp</th><th style='width: 25%%; padding: 5px 0 5px 5px;'>rhost - IP</th><th>ruser - Username</th></tr></table></div>");
}
?>
</div>
</data> <!-- grid data div close -->
<footer> <!-- grid footer div -->
<!-- include page footer -->
<?php include 'footer.php'; ?>
</footer> <!-- grid footer div close -->
</div> <!-- gridlayout close -->
All of the various divs caused no problem causing the page to recalculate/reflow until I included another form to allow CIDR/IP Range calculations to be done on the same page (I was forever having to do that for whois data that didn't provide a CIDR). Adding this additional form, even if it remains hidden and unused causes the entire grid to be redrawn when a query is submitted.
This redrawing occurs even if the only additional information in the <div> is a single line of text, as short and simple as "Boo!". For what it is worth, here is the additional form I attempt to include with <?php include './include/frm-cidr.php'; ?> (and note the commented "Boo!" which can be the only text in the <div> and reflow still occurs):
<div id='frmcidr' class='frmtext' style='display: none;'>
<!-- <p><b>Boo!</b></p> -->
<form id='frmcidr01'
class='frmtext'
action='<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>'
method='post'>
<p class='frmhdg'>IP Range/CIDR Conversion</p>
<div class="frmcontainer">
<table style='width: 100%;'>
<tr>
<td style='width: 15%;'>
<label for='ipstart'>Start IP</label>
</td><td>
<input type='text'
id='ipstart'
name='ipstart'
placeholder = 'Enter Beginning IP Address'>
</td>
</tr><tr>
<td>
<label for='ipend'>End IP</label>
</td><td>
<input type='text'
id='ipend'
name='ipend'
placeholder='Enter Ending IP Address'>
</td>
</tr><tr>
<td> </td>
<td style='padding-top: 15px;'>
<button id='btnip01' type='submit' class='btntext' name='btnip01' value='true'>CIDR</button>
</td>
</tr>
</table>
<!-- <hr style="width: 80%;"> -->
<table style='width: 100%; padding-top: 10px;'>
<tr>
<td style='width: 15%;'>
<label for='cidrentry'>CIDR</label>
</td><td>
<input type='text'
id='cidrentry'
name='cidrentry'
placeholder = 'Enter CIDR'>
</td>
</tr><tr>
<td> </td>
<td style='padding-top: 15px;'>
<button id='btncidr01' type='submit' class='btntext' name='btncidr01' value='true'>IP Range</button>
</td>
</tr>
</table>
</div>
<p style='margin: 0; padding: 0;'> </p>
</form>
</div>
However, also note, that removing this div, not matter whether it just contains "Boo!" or the full form -- no reflow of the grid/page occurs and only the individual data values in the tables are updated as they should be. So it is the inclusion of this one line <?php include './include/frm-cidr.php'; ?> that causes the reflows to start on the next normal "quick query" (or any other) form submission. Remove that included div/form and all is well.
I've been though the suggestions of ensuring .position isn't left as default so that min-height/min-width can be set as the suggested work-around, I've set the values large enough to encapsulate the largest item in the left area and also tried at 0. Other than changing the original size of the left/right grid areas, it has no effect on preventing the page reflow. I've been though the grid documentation on MDN and W3schools and about every "prevent reflow" article I can find, but nothing makes much sense.
I don't do a lot of web development, mostly just for tools for personal use, so I'm not 100% sure I understand all the reasons that a reflow will be triggered other than a size issue the rendering engine thinks it needs to redraw everything for.
There are no errors or warning shown on page inspection, so it isn't a syntax or JS or header or CSS issue. It literally seems like I've hit a "one-div-too-many" threshold by adding the additional form (even if it just contains the one-line "Boo!")
I apologize for the question length, but wanted to include as close to a Minimum Reproducible Example as possible (the site isn't public, so there is no way to provide the full site) What I can't understand is why, if I commented out everything in the div that is include except the text Boo! -- why that makes a difference on causing reflow when a query is submitted -- even if that newly added div remains hidden? That is a much smaller inclusion than any of the other divs in the left grid area that do not cause any reflow.
What else do I need to check or provide to help figure out why this tiny addition cause full grid reflow when any of the existing forms are submitted?
The following is the generated HTML (it's a bit rough, but not that much) and I'll include the site CSS file afterwards (the full grid.css is already posted above). I have to link the files, as they make the character limit exceed the 30000 allowed (but not by too much).
Full Generated HTML
journal-grid-generated html (link valid until Sept. 9, 2025)
Full site.css
site.css (link valid until Sept. 9, 2025)
Full site.js
site.js (link valid until Sept. 13, 2025)




divand css classes, but it just seems something else is taking place. The puzzling part is adding adivcontaining only static text like "Boo" (or whatever) is all that it needed to cause the page to redraw on form update. That is the puzzler.site.cssfile. (the fullgrid.cssfile is already posted). I tried including them in the question, but the generated html is about 500 lines and then css about 200 lines so it exceeded the 30,000 character limit. Let me know if you need to see anything else. The site isn't reachable publicly, so other than the DB credentials, there isn't anything sensitive. PHP doesn't disclose the credentials anyway. The rest is just IP info on bad-actors trying to crack my public server.