Zebra Striping a Flexbox Table with Wrapping Items

Zebra striping a flexbox table with wrapping items

Ideally, the selector you want would target the even values contained in the style attribute. Something like this:

.Rtable > div[style*="order"][style*={even}] { ... }

Basically, this fantasy selector says: target all divs with a style attribute that contains (*) the values "order" and an even number.

It could be simplified to just:

.Rtable > div[style*={even}] { ... }

But this sort of CSS magic doesn't exist, to my knowledge. (CSS Selectors 3 complete list)

Selectors 4 offers an enhanced :nth-child() pseudo-class, which may be able to accomplish such zebra striping. But this isn't ready for prime time.

For now, I would say the simplest CSS method for accomplishing your goal...

I am looking for the simplest way to zebra stripe rows in the following responsive flexbox table.

... would be to add a class to each element with an even order value.

And with a slight adjustment to your media query, the zebra striping works on different screen sizes.

.Rtable {  display: flex;  flex-wrap: wrap;}
.Rtable-cell { box-sizing: border-box; flex: 33.33%; margin: -1px 0 0 -1px; padding: 5px 10px; border: solid 1px slategrey;}
h3 { margin: 0; }
/* NEW */.stripe { background-color: black; color: white;}
/* ADJUSTED */@media all and (max-width: 500px) { .Rtable { display: block; } .stripe { background-color: white; color: black; } .Rtable-cell:nth-child(even) { background-color: black; color: white;}}
<div class="Rtable">
<div style="order:1;" class="Rtable-cell"><h3>Eddard Stark</h3></div> <div style="order:2;" class="Rtable-cell stripe">Has a sword named Ice</div> <div style="order:3;" class="Rtable-cell">No direwolf</div> <div style="order:4;" class="Rtable-cell stripe">Male</div> <div style="order:5;" class="Rtable-cell"><strong>Lord of Winterfell</strong></div>
<div style="order:1;" class="Rtable-cell"><h3>Jon Snow</h3></div> <div style="order:2;" class="Rtable-cell stripe">Has a sword named Longclaw</div> <div style="order:3;" class="Rtable-cell">Direwolf: Ghost</div> <div style="order:4;" class="Rtable-cell stripe">Male</div> <div style="order:5;" class="Rtable-cell"><strong>Knows nothing</strong></div>
<div style="order:1;" class="Rtable-cell"><h3>Arya Stark</h3></div> <div style="order:2;" class="Rtable-cell stripe">Has a sword named Needle</div> <div style="order:3;" class="Rtable-cell">Direwolf: Nymeria</div> <div style="order:4;" class="Rtable-cell stripe">Female</div> <div style="order:5;" class="Rtable-cell"><strong>No one</strong></div>
</div>

zebra striping rows in a flex container with flex-wrap:wrap

Okay, this is actually a fairly difficult task with flexboxes. The best way I could come up with is to use javascript to find out where the wrapping is happening by looping through and comparing the heights of the items. Currently, it is in a self-executing function and will only run on window load. If you want it to be responsive when someone changes a browser size after load then put it inside of a function and call that function on window resize.

Otherwise here is everything.

(function() {
var x = 0;
var counter = 0;
var boxesPerRow = 0;
var elements = document.getElementsByClassName("item");
var totalBoxes = elements.length;
// Loop to find out how many boxes per row
for (var i = 0; i < totalBoxes-2; i++){
x = i+1;
var temp = elements[i].getBoundingClientRect();
if (x <= elements.length)
{
var next = elements[x].getBoundingClientRect();
// Compare height of current vs the next box
if (next.top > temp.top && counter ==0)
{

boxesPerRow = x;
counter = 1;
}
}
}

// var marker is where we are applying style
// var countUpTo is the last box in the row we are styling
const boxes = boxesPerRow;
var countUpTo = boxesPerRow;
var counter = 0;

// Loop through and apply color to boxes.
for(var marker = 0; marker < totalBoxes; marker++)
{
if(marker < countUpTo)
{
elements[marker].style.backgroundColor = "red";

}
else
{
counter++;
if(counter === 1)
{
countUpTo = boxes*(counter+2);
}
else{
countUpTo = countUpTo + (boxes*2);
}

marker = marker+boxes-1;

// Handles buttom row not being a full set of boxes.
if(marker> totalBoxes && !(marker > totalBoxes-(boxes*2)))
{

var leftOver = marker-totalBoxes;

for(var c = 1; c <= leftOver; c++)
{

elements[(totalBoxes-c)].style.backgroundColor = "red";
}
}
}
}

})();

Is there a simple way to know how many rows are in a wrapped Flexbox?

Considering that CSS has no way of knowing when an element wraps, a scripted solution is probably your best bet, unless the number of items per row is consistent throughout the container. Then you may be able to match individual items with CSS nth-child pseudo-classes, which could result in color-coded rows.

Why is my zebra-striping CSS not applying to my table rows?

As your HTML markup is pulled in within your codepen I'm not sure how useful this is to future users but in the context of your codepen your selectors are incorrect.

Your tbody does not contain tr siblings but each tr is wrapped into a div, hence you need to target those instead.

tbody>div:nth-child(even)
background-color: rgba(0,100,1,0.5)

tbody>div:nth-child(odd)
background-color: rgba(0,100,1,0.3)

See updated CodePen

'use strict';
var /*var Leaderboard = React.createClass({ getInitialState: function(){ /*var recentCampers= []; var allTimeCampers= [];*///return {toggle30: 'true'};/*return {recentCampers: [],allTimeCampers: [],toggle30: 'true'};this.recentCampers= this.recentCampers.bind(this);this.allTimeCampers.bind(this);this.toggle30= this.toggle30.bind(this); *//* }, componentDidMount: function(){return axios.all([this.getTopRecentCampers(),this.getTopAlltimeCampers()]).then(function(arr){return {recentCampers: arr[0].data,allTimeCampers: arr[1].data};console.log(this.state.recentCampers[0].username); })},/*getTopRecentCampers: function(){return axios.get('https://fcctop100.herokuapp.com/api/fccusers/top/recent');}, getTopAlltimeCampers: function(){return axios.get('https://fcctop100.herokuapp.com/api/fccusers/top/alltime');},
toggleViewOne: function(){this.setState({toggle30: 'true'});},
toggleViewTwo: function(){this.setState({toggle30: 'false'});}, /* render: function(){return (<div> <h6>Sort by:</h6> <button onClick={this.toggleViewOne} className="btn">Points in past 30 days  <span className="fa fa-sort-desc"> </span></button> <button onClick={this.toggleViewTwo} className="btn"> All time points  <span className="fa fa-sort-desc"> </span> </button> <h1> freeCodeCamp <span className="fa fa-free-code-camp"> </span> </h1> <hr></hr> <div className="table-responsive"> <table className="table"> <thead> <tr> <th> <td> # </td> <td> Camper name </td> <td> Points in last month </td> <td> All time points </td> </th> </tr> </thead> <tbody>//</tbody> </table> </div> </div>);} *//* });var Tbody = React.createClass({render: function(){return (<tbody> {} </tbody>);} });*//*</tr> <tr> <td> gtew</td> </tr> <tr> <td> gffs</td> </tr>this.recentCampers= this.recentCampers.bind(this);this.allTimeCampers.bind(this) *//*ReactDOM.render(<Leaderboard />,document.getElementById('freeCodeCamp'));---------------------------------------------------------------------------- */Leaderboard = React.createClass({ displayName: 'Leaderboard',
getInitialState: function getInitialState() { return { recentCampers: [], allTimeCampers: [], toggle: 'recentCampers' }; },
componentWillMount: function componentWillMount() { return this.getRequest(); },
getRequest: function getRequest() { return axios.all([this.getTopRecentCampers(), this.getTopAlltimeCampers()]).then(function (arr) { this.setState({ recentCampers: arr[0].data, allTimeCampers: arr[1].data }); /*var recent= []; recent= this.state.recentCampers; for (var i=0;i>=100;i++){return recent[i];}; //console.log(this.state.recentCampers[0].username);*/ }.bind(this)); },
getTopRecentCampers: function getTopRecentCampers() { return axios.get('https://fcctop100.herokuapp.com/api/fccusers/top/recent'); },
getTopAlltimeCampers: function getTopAlltimeCampers() { return axios.get('https://fcctop100.herokuapp.com/api/fccusers/top/alltime'); },
toggleViewOne: function toggleViewOne() { this.setState({ toggle: 'recentCampers' }); },
toggleViewTwo: function toggleViewTwo() { this.setState({ toggle: 'allTimeCampers' }); },
render: function render() { return React.createElement( 'div', null, ' ', ' ', React.createElement( 'h6', null, 'Sort by:' ), ' ', React.createElement( 'button', { onClick: this.toggleViewOne, className: 'btn' }, 'Points in past 30 days ', React.createElement( 'span', { className: 'fa fa-sort-desc' }, ' ' ) ), ' ', React.createElement( 'button', { onClick: this.toggleViewTwo, className: 'btn' }, ' All time points ', React.createElement( 'span', { className: 'fa fa-sort-desc' }, ' ' ), ' ' ), ' ', React.createElement( 'h1', null, ' freeCodeCamp ', React.createElement( 'span', { className: 'fa fa-free-code-camp' }, ' ' ), ' ' ), ' ', React.createElement('hr', null), ' ', React.createElement( 'div', { className: 'table-responsive' }, ' ', React.createElement( 'table', { className: 'table' }, ' ', React.createElement( 'thead', null, ' ', React.createElement( 'tr', null, ' ', React.createElement( 'th', null, ' ', React.createElement( 'td', { style: { width: '80px' }, className: 'text-center' }, ' # ' ), ' ', React.createElement( 'td', { style: { width: '600px' } }, ' Camper name ' ), ' ', React.createElement( 'td', { style: { width: '400px' }, className: 'text-center' }, ' Points in last month ' ), ' ', React.createElement( 'td', { style: { width: '400px' }, className: 'text-center' }, ' All time points ' ), ' ' ), ' ' ), ' ' ), ' ', React.createElement( 'tbody', null, ' ', React.createElement(Map, { data: this.state[this.state.toggle] }) ), ' ' ), ' ' ), ' ' ); }});//{<Tbody data={this.state}/* -------------------------------------------------------------------------- */var Map = React.createClass({ displayName: 'Map',
render: function render() { var rows = this.props.data.map(function (row, index) { return React.createElement(Tbody, { rank: index + 1, data: row }); });
return React.createElement( 'tbody', { id: 'stripe' }, rows ); }});/* -------------------------------------------------------------------------- */var Tbody = React.createClass({ displayName: 'Tbody',
render: function render() { return React.createElement( 'div', null, ' ', /*<h1>{this.props.data.username}</h1>*/React.createElement( 'tr', null, React.createElement( 'td', { style: { width: '80px' }, className: 'text-center' }, this.props.rank + '.' ), React.createElement( 'td', { style: { width: '600px' } }, React.createElement( 'a', { target: '_blank', href: 'http://freecodecamp.com/' + this.props.data.username }, React.createElement('img', { src: this.props.data.img }), ' ', React.createElement( 'span', null, this.props.data.username ) ) ), React.createElement( 'td', { id: 'recent', className: 'text-center' }, this.props.data.recent ), React.createElement( 'td', { id: 'all', className: 'text-center' }, this.props.data.alltime ) ), ' ' ); //console.log(JSON.stringify(this.props));*/ }});/* -------------------------------------------------------------------------- */ReactDOM.render(React.createElement(Leaderboard, null), document.getElementById('freeCodeCamp'));
* {  margin: 0;  padding: 0;  box-sizing: border-box;}
body { background-image: linear-gradient(161deg, #006401 0%, #FCFFEE 100%); background-attachment: fixed; color: #502d17; margin-left: 20px; margin-right: 20px;}
button { margin: 5px; background-image: -webkit-linear-gradient(#006401, #FCFFEE); background-attachment: fixed; color: #FCFFEE; box-shadow: inset -1px -3px 10px 1px #515151; margin-bottom: 20px;}
button:active { transform: translate(0, 3px); box-shadow: none; text-decoration: none; outline: none;}button:hover, button:active, button:visited { text-decoration: none; outline: none;}
h6 { margin-left: 10px; margin-top: 15px; margin-bottom: 5px;}
h1 { color: rgba(245, 245, 245, 0.5); margin-left: 5px;}
.fa-free-code-camp { font-size: 1em; color: rgba(245, 245, 245, 0.7);}
table, td { border: 1px solid grey; table-layout: fixed;}
tbody>div:nth-child(even){ background-color: rgba(0, 100, 1, 0.5);}
tbody>div:nth-child(odd) { background-color: rgba(0, 100, 1, 0.3);}
img { border-radius: 100%; height: 60px;}
a { text-decoration: none; color: #502d17;}
#all { max-width: 280px; min-width: 280px;}
#recent { max-width: 280px; min-width: 280px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.8.1/axios.min.js"></script><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/><link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/><div id="freeCodeCamp"></div>

:nth-child(2n) of [attribute=value]

If the question is how to select all the odd elements with a particular attribute ?, then it is possible how explained in the other answers, with

li[data-status="1"]:nth-child(2n+1) {
background: #f00;
}

or in an even easier way:

li[data-status="1"]:nth-child(odd) {
background: #f00;
}

Take a look at this good article on how nth-child works.

If, instead, the question is how to select all the elements with a particular attribute, and then pick only the odd of that sub-list ? , well, that is not yet possible with CSS, but it will with CSS Selectors Level 4, as explained here, with the nth-match() pseudo-class:

:nth-match(An+B of <selector>)

that in your case would be

li:nth-match(2n+1 of [data-status="1"])

or

li:nth-match(odd of [data-status="1"])

Let's wait... CSS4 is coming !! :P


EDIT: as reported by Michael_B, this feature has been stripped by CSS4 specifications, so stop waiting and start figuring another way to do it :/



Related Topics



Leave a reply



Submit