用鼠标动态调整CSS网格布局中的列大小

44

我试图通过拖动列分隔符(或调整占位符)来动态调整CSS网格布局框的大小。

我在nav元素上设置了resize:horizontal;以进行调整大小,当我拖动元素右下角的小调整柄时它会被调整大小,但相邻列的宽度不会自动调整,导致重叠。这里是一个有问题的codepen

HTML:

<main>
 <nav>#1</nav>
 <header>#2</header>
 <section>#3</section>
</main>

CSS

main {
    display: grid;
    border: 3px dotted red;
    grid-gap: 3px;
    grid-template-columns: 200px 1fr;
    grid-template-rows: 100px 1fr;
    height: 100%;
}

nav {
    grid-column: 1;
    grid-row: 1;
    grid-row: 1 / span 2;
    resize: horizontal;
    overflow: scroll;
    border: 3px dotted blue;
}

我本以为css网格引擎会自动处理这种情况,但显然它没有。

我尝试了jquery-ui resizable,但它似乎与css网格不兼容。

我正在研究如何通过使用jquery将grid属性 grid-template-columns / rows:设置为动态值来实现它,但不清楚如何捕获通过调整大小手柄调整元素时抛出的事件。 jQuery resize 事件仅在window对象上触发,而不是dom元素。

有什么办法可以做到这一点,而不必处理像dragstart / dragend这样的低级鼠标事件吗?


很好的问题。我认为可调整大小的网格区域一旦这项技术真正普及,将成为一个重要的事情。至于为什么当您拖出第一列时第二列不重新调整大小,我认为这是因为该操作不会重新排版文档。这里有一个更完整的解释:https://dev59.com/8VoU5IYBdhLWcg3we2-6 - Michael Benjamin
@Dekel:谢谢!我会尝试看看它是否可以适应CSS网格布局;否则可能需要切换到使用浮动。 - ccpizza
2个回答

55
您想要实现的目标可以仅使用CSS来实现。我修改了您的示例,主要要点如下:
1. 最重要的是,尽量不要在语义布局标签中插入原始内容。应该使用标题、段落和列表标签而不是文本和换行符标签。这样可以使代码更易于阅读和理解。很多问题都是因为网格区域中的回流处理方式导致的。
2. 使用grid-template来简化布局,这将使后续断点回流更容易。
3. 使用overflow: auto;以及指定resize: vertical/horizontal。如果不设置overflow,resize会失败。
4. 使用最小/最大宽度/高度值来创建调整大小的边界。

body {
    margin: 10px;
    height: 100%;
}

main {
    display: grid;
    border: 3px dotted red;
    padding: 3px;
    grid-gap: 3px;
    
    grid-template: 
    "nav head" min-content
    "nav main" 1fr
        / min-content 1fr;
}

nav {
    grid-area: nav;
    border: 3px dotted blue;
    overflow: auto;
    resize: horizontal;
    
    min-width: 120px;
    max-width: 50vw;
}

header {
    grid-area: head;
    border: 3px dotted orange;
    overflow: auto;
    resize: vertical;
    
    min-height: min-content;
    max-height: 200px;
}

section {
    grid-area: main;
    border: 3px dotted gray;
}
<main>
  <nav>
    <ul>
      <li>Nav Item</li>
      <li>Nav Item</li>
      <li>Nav Item</li>
      <li>Nav Item</li>
      <li>Nav Item</li>
      <li>Nav Item</li>
    </ul>
  </nav>

  <header>
    <h1>Header Title</h1>
    <small>Header Subtitle</small>
  </header>

  <section>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </section>
</main>


20
能否拖动整个边框,而不仅限于右下角? - Alan Tygel
1
是的:$( ".selector" ).resizable({handles: "e, w"}); - Alan Tygel
8
作为这个回答纯CSS的一部分,让人沮丧的是你找到的解决拖动边框问题的方法涉及jQuery。我仍在寻找用最少或没有额外标记、没有依赖(如果可能)的正确方法来实现这一点(网格布局+完整边框调整处理程序)... - Nicolas Le Thierry d'Ennequin
1
@ErkkaMutanen 这行代码 "nav main" 1fr 将包含主要元素的行高设置为 1fr(即使用剩余高度)。这行代码 / min-content 1fr 设置了两个元素的宽度(即将 nav 元素的宽度设置为 min-content,将 headmain 的宽度设置为 1fr)。 - Peter
3
到了2022年,CSS仍然没有提供像这样的边框调整语法:border-left: 2px solid #ccc resizable,但我们可以在三维空间中旋转元素甚至改变透视。这就是网络世界中惊人的事情。 - pouya
显示剩余3条评论

16
解决方案是不使用显式的固定列宽(grid-template-columns: 200px 1fr;),而是使用相对列宽,例如grid-template-columns: 0.2fr 1fr;——那么网格CSS引擎将处理相邻框的调整大小。接下来是在网格框内添加嵌套div,将它们的min-height / width设置为100%,并通过例如jqueryui可调整大小或其他库使它们可调整大小。

固定的jsfiddle

/* Javscript */

$('.left_inner').resizable();
$('.right_top_inner').resizable();
$('.right_bottom_inner').resizable();
/* CSS */

    .grid {
        display: grid;
        grid-template-columns: 0.2fr 1fr;
        grid-template-rows: 1fr 4fr;
        grid-gap: 3px;
        position: relative;
    }

    .left {
        grid-row: 1 / span 2;
    }

    .right_top {
        grid-column: 2;
        grid-row: 1;
    }

    .right_bottom {
        grid-column: 2;
        grid-row: 2;
    }

    .left_inner {
        background-color: #fedcd2;
        padding: 0;
        width: 100%;
        min-width: 100%;
        height: 100%;
        min-height: 100%;
        text-align: center;
    }

    .right_top_inner {
        background-color: #f9cf00;
        padding: 0;
        width: 100%;
        min-width: 100%;
        height: 100%;
        min-height: 100%;
        text-align: center;
    }

    .right_bottom_inner {
        background-color: #f8eee7;
        padding: 0;
        width: 100%;
        min-width: 100%;
        height: 100%;
        min-height: 100%;
        text-align: center;
    }
<!-- HTML -->

<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/css/smoothness/jquery-ui-1.10.0.custom.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/jquery-ui.js"></script>

    <main class="grid">
     <aside class='left'>
      <div class="left_inner">
        drag the bottom right handle to resize
      </div>
     </aside>
     <section class="right_top">
      <div class="right_top_inner">right_top_inner</div>
     </section>
     <section class="right_bottom">
      <div class="right_bottom_inner">right_bottom_inner</div>
     </section>
    </main>

❗️虽然这在最简单的情况下可以运行,但在实际用例中会出现问题。我尝试了jquery-ui布局,它在某种程度上效果更好(这里有一个演示),但这个库已经过时且在处理框架时存在问题,所以我选择了angular-split-pane(基于angular 1)。这个库运行良好且体积更小。(更新:该项目目前已被放弃,可能不是最佳选择)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接