簡體   English   中英

如何在 CSS 多列布局中插入分欄符?

[英]How can I insert column break in a CSS multi-column layout?

我正在嘗試實現一個超級菜單。

菜單項的數量是可變的。 默認情況下,它們必須在4 列中呈現,平衡(每列上的項目數應與其他列幾乎相同)。 超級菜單的高度也是可變的,基於其內容。

我已經用CSS Multi-Column layout實現了它。

代碼如下:

.menu {
  -webkit-column-count: 4;
     -moz-column-count: 4;
          column-count: 4;
 -webkit-column-gap: 32px;
    -moz-column-gap: 32px;
         column-gap: 32px;
}

我的問題是有一個特殊的菜單項類型,它應該作為一個分欄符 這個菜單項類型是可選的,但如果存在,它應該強制瀏覽器開始一個新的列來顯示內容(最多可以有 3 個分欄符)。

我添加了以下 css 代碼:

.menu-item--column-break {
    display: block;
    -webkit-column-break-before: column;
              -moz-break-before: column;
                   break-before: column;
}

但此 CSS 僅適用於 Chrome:

在此處輸入圖片說明

Firefox 和 Safari 不支持“column-break”元素的 CSS 規則,它像普通菜單項一樣顯示: 在此處輸入圖片說明

菜單是從 JSON 對象在 JavaScript 中生成的,可以更改 HTML,但我更喜歡僅使用 CSS/JS 的解決方案。

您對我如何在所有瀏覽器中實現這一點有任何想法嗎?

這是完整的代碼:

https://codepen.io/andreivictor/pen/ywLJKx

或者

 let items = [ {title: 'Category 1', type: 'menu-item'}, {title: 'Category 2', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 3', type: 'menu-item'}, {title: 'Category 4', type: 'menu-item'}, {title: 'Category 5', type: 'menu-item'}, {title: 'Category 6', type: 'menu-item'}, {title: 'Category 7', type: 'menu-item'}, {title: 'Category 8', type: 'menu-item'}, {title: 'Category 9', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 10', type: 'menu-item'}, {title: 'Category 11', type: 'menu-item'}, {title: 'Category 12', type: 'menu-item'}, {title: 'Category 13', type: 'menu-item'}, {title: 'Category 14', type: 'menu-item'}, {title: 'Category 15', type: 'menu-item'}, {title: 'Category 16', type: 'menu-item'}, {title: 'Category 17', type: 'menu-item'}, {title: 'Category 18', type: 'menu-item'}, {title: 'Category 19', type: 'menu-item'}, {title: 'Category 20', type: 'menu-item'}, {title: 'Category 21', type: 'menu-item'}, ]; const $menu = document.querySelector('.menu'); console.log( $menu ); items.forEach((item) => { let nodeItem = document.createElement("div"); nodeItem.classList.add('menu-item'); let nodeItemText = document.createTextNode(item.title); nodeItem.appendChild(nodeItemText); if (item.type === 'column-break') { nodeItem.classList.add('menu-item--column-break'); } $menu.appendChild(nodeItem); });
 .menu { position: relative; padding: 0 16px; -webkit-column-count: 4; -moz-column-count: 4; column-count: 4; -moz-column-rule: 1px solid #e2e1e1; column-rule: 1px solid #e2e1e1; -webkit-column-gap: 32px; -moz-column-gap: 32px; column-gap: 32px; } .menu-item--column-break { display: block; -webkit-column-break-after: column; -moz-break-after: column; break-after: column; color: red; }
 <div class="container"> <div class="menu"> </div> </div>

為了解決這個問題,我決定采用js方法。 它並不完全優雅,因為在js代碼中,您需要假設您知道單個菜單項的高度。 但這解決了這個問題,也許它適合你的項目。 我的想法是,我將菜單的顯示更改為一個flexbox,它將項目放在一列中,但在沒有空格時包裝到下一列。 現在,為了能夠耗盡空間和包裝,我們需要兩件事:一個固定的菜單高度(這就是為什么我根據提供的項目計算它)以及一個不可伸展的元素,它將伸展到100%的高度(它確實不適合當前列或下一列,因此它自己創建一個0px寬的列​​,因此充當列分隔符)。 看看這個解決方案:

 let items = [ {title: 'Category 1', type: 'menu-item'}, {title: 'Category 2', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 3', type: 'menu-item'}, {title: 'Category 4', type: 'menu-item'}, {title: 'Category 5', type: 'menu-item'}, {title: 'Category 6', type: 'menu-item'}, {title: 'Category 7', type: 'menu-item'}, {title: 'Category 8', type: 'menu-item'}, {title: 'Category 9', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 10', type: 'menu-item'}, {title: 'Category 11', type: 'menu-item'}, {title: 'Category 12', type: 'menu-item'}, {title: 'Category 13', type: 'menu-item'}, {title: 'Category 14', type: 'menu-item'}, {title: 'Category 15', type: 'menu-item'}, {title: 'Category 16', type: 'menu-item'}, {title: 'Category 17', type: 'menu-item'}, {title: 'Category 18', type: 'menu-item'}, {title: 'Category 19', type: 'menu-item'}, {title: 'Category 20', type: 'menu-item'}, {title: 'Category 21', type: 'menu-item'}, ]; const $menu = document.querySelector('.menu'); console.log( $menu ); var longestColumnLength = 0; var currentColumnLength = 0; var numberOfBreaks = 0; items.forEach((item) => { currentColumnLength++; let nodeItem = document.createElement("div"); nodeItem.classList.add('menu-item'); let nodeItemText = document.createTextNode(item.title); nodeItem.appendChild(nodeItemText); if (item.type === 'column-break') { nodeItem.classList.add('menu-item--column-break'); let breaker = document.createElement("div"); breaker.classList.add('menu-item--column-break-line'); $menu.appendChild(nodeItem); $menu.appendChild(breaker); longestColumnLength = Math.max(longestColumnLength, currentColumnLength); currentColumnLength = 0; numberOfBreaks++; } else { $menu.appendChild(nodeItem); } }); var availableNaturalColumnsAtTheEnd = Math.max(1, 4 - numberOfBreaks); var maxLengthOfRemainingItems = currentColumnLength / availableNaturalColumnsAtTheEnd; var actualLongestColumn = Math.max(longestColumnLength, maxLengthOfRemainingItems) $menu.setAttribute("style", "height: " + actualLongestColumn*20 + "px") 
 .menu { position: relative; padding: 0 16px; display: flex; flex-direction: column; flex-wrap: wrap; height: 200px; } .menu-item{ height: 20px; } .menu-item--column-break { display: block; color: red; } .menu-item--column-break-line { height: 100%; width: 0; overflow: hidden; } 
 <div class="container"> <div class="menu"> </div> </div> 

哦,還有一件事。 我不確定你是否真的想要顯示“--- cb ---”項目。 我把它們留在了解決方案中但是如果你想擺脫它們,你可以通過刪除我的額外列分隔符來輕松修改代碼,而是讓你的“--- cb ---”項目作為列分隔符。

我正在考慮這個問題並想出了另一個解決方案。 基本上問題是不支持多列中斷,因此目前不可能只為所有瀏覽器創建那些固定列和動態列。 因此我決定將問題分成兩部分。 我根據固定的休息時間將項目分組。 我假設每個小組都是一個首發列。 然后我看到我有多少列。 如果它小於4(您想要的列數),那么我允許最大的組動態地分成一列。 我繼續這個,直到我達到總共4列 - 無論是固定的還是動態的,或兩者兼而有之。

請參閱下面的代碼段。

此外,通過添加,刪除或移動休息來玩剪切。 它應該適用於許多不同的場景。

 let items = [ {title: 'Category 1', type: 'menu-item'}, {title: 'Category 2', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 3', type: 'menu-item'}, {title: 'Category 4', type: 'menu-item'}, {title: 'Category 5', type: 'menu-item'}, {title: 'Category 6', type: 'menu-item'}, {title: 'Category 7', type: 'menu-item'}, {title: 'Category 8', type: 'menu-item'}, {title: 'Category 9', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 10', type: 'menu-item'}, {title: 'Category 11', type: 'menu-item'}, {title: 'Category 12', type: 'menu-item'}, {title: 'Category 13', type: 'menu-item'}, {title: 'Category 14', type: 'menu-item'}, {title: 'Category 15', type: 'menu-item'}, //{title: '---cb---', type: 'column-break'}, {title: 'Category 16', type: 'menu-item'}, {title: 'Category 17', type: 'menu-item'}, {title: 'Category 18', type: 'menu-item'}, {title: 'Category 19', type: 'menu-item'}, {title: 'Category 20', type: 'menu-item'}, {title: 'Category 21', type: 'menu-item'} ]; const $menu = document.querySelector('.menu'); var allGroups = []; var currentGroup = 0; allGroups.push({ items: [], columns: 1}); function addGroup($menu, group, numberOfColumns){ let columnItem = document.createElement("div"); columnItem.classList.add('menu-group'); if(numberOfColumns === 1){ columnItem.classList.add('fixed'); } else { columnItem.classList.add('dynamic-columns'); var style = '-webkit-column-count: ' + numberOfColumns + ';'; style += '-moz-column-count: ' + numberOfColumns + ';'; style += 'column-count: ' + numberOfColumns + ';'; columnItem.setAttribute('style', style); } group.forEach((groupItem) => { columnItem.appendChild(groupItem); }); $menu.appendChild(columnItem); }; var columnsCount = 1; items.forEach((item) => { let nodeItem = document.createElement("div"); allGroups[currentGroup].items.push(nodeItem); nodeItem.classList.add('menu-item'); let nodeItemText = document.createTextNode(item.title); nodeItem.appendChild(nodeItemText); if (item.type === 'column-break') { nodeItem.classList.add('menu-item--column-break'); //addGroup($menu, currentGroup, 1); currentGroup++; allGroups.push({ items: [], columns: 1}); columnsCount++; } }); var forSorting = []; allGroups.forEach((item) => { forSorting.push(item); }); while(columnsCount < 4){ forSorting.sort(function(a, b){ return (b.items.length/b.columns) - (a.items.length/a.columns); }); forSorting[0].columns++; columnsCount++; } allGroups.forEach((item) => { addGroup($menu, item.items, item.columns); }); 
 .menu { position: relative; padding: 0 16px; display: flex; flex-direction: row; } .menu-group:not(:last-child){ border-right: 1px solid #e2e1e1; margin-right: 8px; } .menu-group.fixed { flex-basis: calc(25% - 8px); flex-grow: 0; flex-shrink: 0; } .menu-group.dynamic-columns { flex-grow: 1; -moz-column-rule: 1px solid #e2e1e1; column-rule: 1px solid #e2e1e1; } .menu-item--column-break { display: block; color: red; } 
 <div class="container"> <div class="menu"> </div> </div> 

一個hacky方法是在元素中斷之后為下一個元素添加一個大的margin-top ,這將強制這個元素在新列上開始。

 let items = [ {title: 'Category 1', type: 'menu-item'}, {title: 'Category 2', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 3', type: 'menu-item'}, {title: 'Category 4', type: 'menu-item'}, {title: 'Category 5', type: 'menu-item'}, {title: 'Category 6', type: 'menu-item'}, {title: 'Category 7', type: 'menu-item'}, {title: 'Category 8', type: 'menu-item'}, {title: 'Category 9', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 10', type: 'menu-item'}, {title: 'Category 11', type: 'menu-item'}, {title: 'Category 12', type: 'menu-item'}, {title: 'Category 13', type: 'menu-item'}, {title: 'Category 14', type: 'menu-item'}, {title: 'Category 15', type: 'menu-item'}, {title: 'Category 16', type: 'menu-item'}, {title: 'Category 17', type: 'menu-item'}, {title: 'Category 18', type: 'menu-item'}, {title: 'Category 19', type: 'menu-item'}, {title: 'Category 20', type: 'menu-item'}, {title: 'Category 21', type: 'menu-item'}, ]; const $menu = document.querySelector('.menu'); console.log( $menu ); items.forEach((item) => { let nodeItem = document.createElement("div"); nodeItem.classList.add('menu-item'); let nodeItemText = document.createTextNode(item.title); nodeItem.appendChild(nodeItemText); if (item.type === 'column-break') { nodeItem.classList.add('menu-item--column-break'); } $menu.appendChild(nodeItem); }); 
 .menu { position: relative; padding: 0 16px; -webkit-column-count: 4; -moz-column-count: 4; column-count: 4; -moz-column-rule: 1px solid #e2e1e1; column-rule: 1px solid #e2e1e1; -webkit-column-gap: 32px; -moz-column-gap: 32px; column-gap: 32px; } .menu-item--column-break { display: block; color: red; } .menu-item--column-break+ * { margin-top:100px; } 
 <div class="container"> <div class="menu"> </div> </div> 

唯一的缺點(使其成為黑客)是一個非常大的邊距可能會影響列的長度:

 let items = [ {title: 'Category 1', type: 'menu-item'}, {title: 'Category 2', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 3', type: 'menu-item'}, {title: 'Category 4', type: 'menu-item'}, {title: 'Category 5', type: 'menu-item'}, {title: 'Category 6', type: 'menu-item'}, {title: 'Category 7', type: 'menu-item'}, {title: 'Category 8', type: 'menu-item'}, {title: 'Category 9', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 10', type: 'menu-item'}, {title: 'Category 11', type: 'menu-item'}, {title: 'Category 12', type: 'menu-item'}, {title: 'Category 13', type: 'menu-item'}, {title: 'Category 14', type: 'menu-item'}, {title: 'Category 15', type: 'menu-item'}, {title: 'Category 16', type: 'menu-item'}, {title: 'Category 17', type: 'menu-item'}, {title: 'Category 18', type: 'menu-item'}, {title: 'Category 19', type: 'menu-item'}, {title: 'Category 20', type: 'menu-item'}, {title: 'Category 21', type: 'menu-item'}, ]; const $menu = document.querySelector('.menu'); console.log( $menu ); items.forEach((item) => { let nodeItem = document.createElement("div"); nodeItem.classList.add('menu-item'); let nodeItemText = document.createTextNode(item.title); nodeItem.appendChild(nodeItemText); if (item.type === 'column-break') { nodeItem.classList.add('menu-item--column-break'); } $menu.appendChild(nodeItem); }); 
 .menu { position: relative; padding: 0 16px; -webkit-column-count: 4; -moz-column-count: 4; column-count: 4; -moz-column-rule: 1px solid #e2e1e1; column-rule: 1px solid #e2e1e1; -webkit-column-gap: 32px; -moz-column-gap: 32px; column-gap: 32px; } .menu-item--column-break { display: block; color: red; } .menu-item--column-break+ * { margin-top:500px; } 
 <div class="container"> <div class="menu"> </div> </div> 

順便說一下,如果你在每一欄都有一個休息時它會工作正常

 let items = [ {title: 'Category 1', type: 'menu-item'}, {title: 'Category 2', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 3', type: 'menu-item'}, {title: 'Category 4', type: 'menu-item'}, {title: 'Category 5', type: 'menu-item'}, {title: 'Category 6', type: 'menu-item'}, {title: 'Category 7', type: 'menu-item'}, {title: 'Category 8', type: 'menu-item'}, {title: 'Category 9', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 10', type: 'menu-item'}, {title: 'Category 11', type: 'menu-item'}, {title: 'Category 12', type: 'menu-item'}, {title: 'Category 13', type: 'menu-item'}, {title: 'Category 14', type: 'menu-item'}, {title: 'Category 15', type: 'menu-item'}, {title: 'Category 16', type: 'menu-item'}, {title: 'Category 17', type: 'menu-item'}, {title: '---cb---', type: 'column-break'}, {title: 'Category 19', type: 'menu-item'}, {title: 'Category 20', type: 'menu-item'}, {title: 'Category 21', type: 'menu-item'}, ]; const $menu = document.querySelector('.menu'); console.log( $menu ); items.forEach((item) => { let nodeItem = document.createElement("div"); nodeItem.classList.add('menu-item'); let nodeItemText = document.createTextNode(item.title); nodeItem.appendChild(nodeItemText); if (item.type === 'column-break') { nodeItem.classList.add('menu-item--column-break'); } $menu.appendChild(nodeItem); }); 
 .menu { position: relative; padding: 0 16px; -webkit-column-count: 4; -moz-column-count: 4; column-count: 4; -moz-column-rule: 1px solid #e2e1e1; column-rule: 1px solid #e2e1e1; -webkit-column-gap: 32px; -moz-column-gap: 32px; column-gap: 32px; } .menu-item--column-break { display: block; color: red; } .menu-item--column-break+ * { margin-top:500px; } 
 <div class="container"> <div class="menu"> </div> </div> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM