Home > Enterprise >  (SCSS - CSS) Grab every second child element
(SCSS - CSS) Grab every second child element

Time:11-13

I have a CSS Framework for personal usage, that I want to be as "automatic" as possible. And i've met a issue that I cannot solve, but maybe you can help me.

Let's say I have 3 cards where one is the top parent and the rest are children of eachother. I'll show what I mean.

<div class="card"> // Top parent
  Lorem!
  <div class="card"> // First child
    Lorem!
    <div class="card"> // Second child
      Lorem!
    </div>
  </div>
</div>

My goal is to have it such that the Parent is styled with an opposing background color from the body and the first child to be the same as the body again so it opposes to the parent card. And the second child to be opposed again to the first child, aka the same background as the top parent. And for this to continue infinitely.

It's possible to use set a background colour for each, but instead of doing this manually I wish for this to happen by itself as you add cards inside other cards.

I am currently solving this doing .card > .card but this only works once, and I don't wish to spam the css with .card > .card > .card > .card > .card etc...

I tried using div:nth-child(even) but this doesn't work if you keep doing children inside one another.

How can I make this possible?

Current Code:

SCSS:

@if #{$class} == card {
/* If parent card has a child card */
.#{$class} > .#{$class} {
  background-color: var(--clr-light-primary-background);
 }
}

CSS:

.card > .card {
  background-color: var(--clr-light-primary-background);
 }

HTML:

<div class="card column33 sm-rounded">
        <div class="card-header">
            <h2 class="card-title text-regular text-uppercase">TOP PARENT</h2>
        </div>
        <div class="card">
            <div class="card-header">
                <h2 class="card-title text-medium">1st CHILD</h2>
            </div>
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title text-medium">2nd CHILD</h2>
                </div>
            </div>
        </div>
    </div>

My Goal

CodePudding user response:

Can't thnk of a way to do this with CSS that is dynamic enough so here is some JS.

It starts at the outer level card, and then goes on down through them alternating the background color until there are no more.

let odd = true;
let cards = document.querySelectorAll('.card');
do {
  cards[0].style.backgroundColor = (odd) ? 'blue' : 'red';
  odd = !odd;
  cards = cards[0].querySelectorAll('.card');
} while (cards.length > 0)
<div class="card column33 sm-rounded">
  <div class="card-header">
    <h2 class="card-title text-regular text-uppercase">TOP PARENT</h2>
  </div>
  <div class="card">
    <div class="card-header">
      <h2 class="card-title text-medium">1st CHILD</h2>
    </div>
    <div class="card">
      <div class="card-header">
        <h2 class="card-title text-medium">2nd CHILD</h2>
      </div>
    </div>
  </div>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

CSS solution

This is probably not practical for a CSS framework, but you could play around with CSS filters and see if you can get a colour combination that works.

Note the JS here is just for the demo.

Show code snippet

document.querySelector("button").addEventListener("click", () => {
  const cards = document.querySelectorAll(".card");
  const lastCard = cards[cards.length - 1];

  lastCard.innerHTML = `${cards.length} <div >${
    cards.length   1
  }</div>`;
});

document.querySelector("input").addEventListener("input", (e) => {
  const newColor = e.target.value;

  document.documentElement.style.setProperty("--card-bg-color", newColor);
});
:root {
  --card-bg-color: #666;
}

.card {
  padding: .5rem;
  border-radius: 0.25em;
  background: var(--card-bg-color);
  >
}

.card>.card {
  filter: invert(100%);
}


/* Demo */
.control {
  position: fixed;
  bottom: 0;
  left: 0;
  display: grid;
  grid-gap: .5rem;
  background: black;
  color: white;
  padding: 1rem;
}
<div class="card">1
  <div class="card">2
    <div class="card">3</div>
  </div>
</div>

<!-- Demo -->
<div class="control">
  <button>Add card</button>
  <label for="color">Change colour <input type="color" value="#666666"></label>
</div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

JavaScript solution

Show code snippet

const setCardBackgroundColors = () => {
  const cards = [...document.querySelectorAll(".card")];

  cards && cards.length && cards.forEach((card, index) => {
    index % 2 && card.classList.add('has-bg-dark');
  })
}


// Just for demo
const template = (level) => {
  return `
    <article >
       <p>${level}</p>
    </article>
    `
}
document.querySelector("button").addEventListener("click", () => {
  const cards = document.querySelectorAll(".card");
  const lastCard = cards[cards.length - 1];

  lastCard.insertAdjacentHTML('beforeend', template(cards.length   1));
  setCardBackgroundColors();
});
.card {
  background-color: #555;
  color: white;
  padding: 1rem;
  border-radius: 0.25em;
  position: relative;
}

.card.has-bg-dark {
  background-color: #222;
}

/* demo styles */
* {
  box-sizing: border-box;
}

body {
  font: 18px/1.2 system-ui;
  padding: 1rem;
}

.control {
  position: fixed;
  bottom: 0;
  left: 0;
  display: grid;
  grid-gap: 0.5rem;
  background: black;
  color: white;
  padding: 1rem;
}
<article class="card">
  <p>1</p>
</article>

<!-- Demo -->
<div class="control">
  <button>Add card</button>
</div>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related