简体   繁体   中英

Flexbox: flex-flow column; flex-basis 100% not working without explicit height

I'm building a website for myself with a two-column content layout, where the columns have a 1:2 aspect ratio, and I am trying to avoid using Grid for something that I believe Flexbox can more than easily handle. However, all of my attempts to force a wrap from the narrow left column to the wide right column using flex-basis: 100%; do not work without an explicit, non percentage height set for the parent element. I don't know what to do. I've already used this article and referenced multiple questions for solutions, and literally nothing has worked.

I'm using Firefox 72 and this is supposed to work in recent versions of Firefox.

 :root { --bodywidth: 80vw; --flexbasis: calc(var(--bodywidth) / 8); --spacebasis: calc(var(--flexbasis) / 12); --columnwidth: calc(var(--bodywidth) / 3); } /* https://tobiasahlin.com/blog/flexbox-break-to-new-row/ */ hr.break { border: none; margin: 0; padding: 0; flex-basis: 100%; flex-grow: 1; } hr.row.break { height: 0; } hr.col.break { width: 0; } main { display: flex; flex-flow: column wrap; /* height: 100%; /* << DOES NOT WORK */ /* height: 100vw; /* << Works perfectly fine, but NOT ideal */ } /* vv As a bonus, these rules somehow make everything 2 column widths wide when only the stuff AFTER the break should be that wide */ main:not(.break) { min-width: var(--columnwidth); width: 100%; } main hr.break+* { width: calc(var(--columnwidth) * 2); }
 <main> <section> <h1>About</h1> <p>Blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah.</p> </section> <section> <h1>Disclaimer</h1> <p>Here there be naughty things,,.</p> </section> <.-- The amount of content on both sides of the break varies, as do the dimensions of the sections --> <hr class="col break" /> <article class="blog"> <h1>Blog Entry</h1> <p>Lorem ipsum dolor sit amet. consectetur adipiscing elit, Vestibulum eleifend molestie orci. Donec pellentesque viverra magna, nec viverra velit laoreet non. Etiam blandit erat nulla, semper faucibus eros rhoncus vel.</p> </article> </main>

If I have to, I can hold my nose and use Grid and make it work, but it's not ideal by any stretch of the imagination and would require a whole lot of extra CSS to make it work. I would much rather use Flexbox if anyone has solutions.

grid and mediaquerie is, IMHO, a good way to manage the swhitching from a 1 column layout to a 2 columns layout.

Demo of the idea:

 :root { /* possible use of var here */ --col1: 1fr; --col2: 2fr; } main { display: grid; grid-template-columns: var(--col1) var(--col2); } main> * { border:solid 1px; margin:2px; } section { grid-column:1; } article { grid-column:2; grid-row:1 / 10; } /* breakpoint at 768px */ @media screen and (max-width: 768px) { main { display: flex; flex-flow: column; } main section + section {/* best is to use an id */ order: 1; } }
 <main> <section> <h1>About</h1> <p>Blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah.</p> </section> <section><, -- that one might deserve an id to easy reorder its position --> <h1>Disclaimer</h1> <p>Here there be naughty things..,</p> </section> <article class="blog"> <h1>Blog Entry</h1> <p>Lorem ipsum dolor sit amet. consectetur adipiscing elit, Vestibulum eleifend molestie orci. Donec pellentesque viverra magna, nec viverra velit laoreet non. Etiam blandit erat nulla, semper faucibus eros rhoncus vel.</p> </article> </main>

I was able to figure out a hackish workaround using javascript. This is probably unique to my use case, so I'm not going to flag this as the accepted answer, but I'm publishing it here in case anyone needs it for their own uses: This hack works with the exact same HTML+CSS as above:

var timerID = 0;

// This function saves CPU cycles and user sanity when resizing the browser window!
// It just destroys and creates a timeout as the resize event gets rapid fired
function resizeDelay() {
    if(timerID) clearTimeout(timerID);
    timerID = setTimeout(recomputeMainHeight,500);
}

// THIS function does all the magic!
function recomputeMainHeight(mainElts) {
    // Get the computed margins of the elements in the columns
    var margin = parseInt(window.getComputedStyle(mainElts[0]).marginBottom) * 2;

    // Find the index of the break element
    var breakIndex = 0;
    while(mainElts[breakIndex].nodeName != "HR") breakIndex++;

    /*  The height of the left column is equal to:
        The box bottom coordinate of the break's preceding sibling, MINUS
        The box top coordinate of the first child node in the container, PLUS
        The margin width of the elements in the flex TIMES the number of items BEFORE the break
    */
    var leftColContentHeight =
        mainElts[breakIndex].getBoundingClientRect().bottom -
        mainElts[0].getBoundingClientRect().top +
        margin * breakIndex;
    /*  The height of the right column is equal to:
        The box bottom coordinate of the last child node in the container, MINUS
        The box top coordinate of the break's following sibling, PLUS
        The margin width of the elements in the flex TIMES the number of items AFTER the break
    */
    var rightColContentHeight =
        mainElts[mainElts.length - 1].getBoundingClientRect().bottom -
        mainElts[breakIndex + 1].getBoundingClientRect().top +
        margin * (mainElts.length - 1 - breakIndex);

    //  Set the greater of the two values as the height of the container
    document.querySelector("main").style.height =
        (leftColContentHeight > rightColContentHeight ?
        leftColContentHeight :
        rightColContentHeight) + "px";
}

//  This function sets the widths of everything dependign on their relation to the break
function bootstrap() {
    //  Fetch a list of all fist-gen children of the container
    var mainElts = document.querySelectorAll("main > *");

    var pastBreak = false;
    //  Loop through the NodeList...
    for(elt of mainElts) {
        //  If the loop finds the break element, set the flag and continue
        if(elt.nodeName === "HR") {
            pastBreak = true;
            continue;
        }
        //  If the loop is past the break, set the width to the wide column, else the narrow column
        if(pastBreak) elt.style.width = "calc(var(--columnwidth) * 2)";
        else elt.style.width = "var(--columnwidth)";
    }

    //  Call the function that works the hack magic above
    recomputeMainHeight(mainElts);
}

//  Some event listeners
window.addEventListener('load',bootstrap);
window.addEventListener('resize',resizeDelay);

CAVEATS:

  1. I have not tested this in IE or Safari/WebKit; I've read that Safari/WebKit reports different values for getComputedStyle() so if you want to support Safari/WebKit you'll have to adapt this accordingly. I'll probably have to do so myself when I start testing this page on my fruityPhone and fruityPad.
  2. A small visual bug will be present between Firefox and Chrom(e|ium). Firefox DOES NOT account for margins when elt.getBoundingClientRect() is called and all the numbers are computed. Chrom(e|ium) DOES. I chose to adapt for Firefox only and accept an excess of gutter at the bottom of the page because setting the container height slightly too high is preferable to slightly too low and having stuff overlap each other and the last thing I want to do is wrestle with browser sniffing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM