Skip to main content

CSS nth-child 이용해서 랜덤처럼 보이게 구현하기


사실 자바스크립트를 이용하면 간단하게 랜덤을 구현할 수 있다.

하지만 아래 두 가지를 만족하는 상태에서 각 요소에 대해 랜덤을 구현하는것은 까다롭다.

  • javscript 코드 변경 금지
  • 요소가 얼마나 만들어질지 모름

아래는, 순수하게 css만 이용하여 랜덤하게 보이도록 구현하는 방법이다.



1. 랜덤하게 보이도록 구현하기

우선 부모가 .container, 자식이 모두 .content 클래스명을 가지며, 자식이 얼마나 추가될지 모르는 상태로 가정

<div class="container">
<div class="content"></div>
<div class="content"></div>
<div class="content"></div>
</div>

아래 예시와 같이 scss에서 반복문으로 nth-child를 돌리면 랜덤이 가능하다.

하지만 요소의 최대 개수를 지정해줘야 하기 때문에, 이 값을 초과했을 경우 (예시에서는 11번째 요소부터) 랜덤이 적용되지 않는다.

@for $i from 1 through 10 {
.content:nth-child(#{$i}) {
background-color: rgb(random(256), random(256), random(256));
}
}

그렇다고 최대 개수를 너무 높게 지정하게 되면 컴파일된 css 파일의 크기가 커지기 때문에 비효율적이다.


이때 nth-child함수형 표기법을 이용하면 요소 개수만큼만 적용할 수 있는데, nth-child의 인자로 숫자 대신 An+B 형식으로 넣어주면 된다.

예시 : [n -> 0, 1, 2 ...]

  • :nth-child(n) : 0, 1, 2, 3, ...
  • :nth-child(2n) : 0, 2, 4, 6, ...
  • :nth-child(3n+1) : 1, 4, 7, 10, ...
  • :nth-child(-2n+7) : 7, 5, 3, 1
  • :nth-child(n+3):nth-child(-n+6) : 3, 4, 5, 6

요소 개수에 맞게 css가 적용이 되긴 하지만, 하나만 적용한다면 규칙적인 모습을 가지게 된다.

이를 해결하려면 n, 2n, 3n, ... 이렇게 여러개를 적용해주면 된다. 그 수가 많아질수록 더 랜덤해 보이는 결과가 나타난다.

.content:nth-child(n) {
background-color: red;
}
.content:nth-child(2n) {
background-color: green;
}
.content:nth-child(3n) {
background-color: blue;
}
.content:nth-child(4n) {
background-color: black;
}
.content:nth-child(5n) {
background-color: orange;
}
.content:nth-child(6n) {
background-color: violet;
}
.content:nth-child(7n) {
background-color: cyan;
}
.content:nth-child(8n) {
background-color: yellow;
}
.content:nth-child(9n) {
background-color: gray;
}
.content:nth-child(10n) {
background-color: white;
}

이렇게 되면, nth-child 인자들의 최소공배수 이후패턴반복되게 된다.

1~10까지의 최소공배수, lcm(1,2,3,4,5,6,7,8,9,10) -> 즉 2520번 이후 같은 패턴이 반복되게 된다.


만약 아래와 같이 소수를 활용한다면 같은 양의 코드로 패턴의 길이를 훨씬 늘릴 수 있다.

(또는 같은 길이의 패턴에 코드의 양을 줄일 수 있다.)



2. 소수를 사용하여 효율성 최대화

매미는 수명이 소수인 5, 7, 13, 17년으로, 천적과 마주칠 확률을 낮춘다고 한다.

  • 소수와 자연수의 최소공배수는 두 수의 곱 (자연수가 해당 소수의 배수가 아닐 경우)

수명이 천적 4년, 매미 5년이면, 최소 20년 주기로 한 번씩 마주친다.

수명이 천적 6년, 매미 17년이면, 최소 102년 주기로 한 번씩 마주친다.


또한 매미끼리도 서로 먹이 경쟁을 같이 할 확률을 낮춘다고 한다.

  • 서로소일 경우, 최소공배수는 두수의 곱

매미1 5년, 매미2 7년이면, 최소 35년 주기로 한 번씩 먹이 경쟁을 한다.

매미1 13년, 매미2 17년이면, 최소 221년 주기로 한 번씩 먹이 경쟁을 한다.


이 원리를 css에 적용하면, 같은 양의 코드에서 패턴의 길이를 최대로 가져갈 수 있다.



3. 코드

단순히 부모 요소인 .container 내부에 .content 자식을 반복문으로 추가하는 html, js 코드이다.

<div class="container"></div>
const container = document.querySelector('.container');

for (let i = 0; i < 1000; i++) {
const content = document.createElement('div');
content.classList.add('content');
container.appendChild(content);
}

nth-child 인자로 소수를 사용한 css 코드이다.

.content {
float: left;
width: 13px;
height: 13px;
}
.content:nth-child(n) {
background-color: red;
}
.content:nth-child(2n) {
background-color: green;
}
.content:nth-child(3n) {
background-color: blue;
}
.content:nth-child(5n) {
background-color: black;
}
.content:nth-child(7n) {
background-color: orange;
}
.content:nth-child(11n) {
background-color: violet;
}
.content:nth-child(13n) {
background-color: cyan;
}
.content:nth-child(17n) {
background-color: yellow;
}
.content:nth-child(19n) {
background-color: gray;
}
.content:nth-child(23n) {
background-color: white;
}

만약 scss를 사용할 경우 아래와 같이 사용하면 되며, 색상을 랜덤하게 사용할 수도 있다.

.content {
float: left;
width: 13px;
height: 13px;
}

$prime_num: 1, 2, 3, 5, 7, 11, 13, 17, 19, 23;
@for $i from 1 through length($prime_num) {
.content:nth-child(#{nth($prime_num, $i)}n) {
background-color: rgb(random(256), random(256), random(256));
}
}

이 경우, 소수는 모두 서로소이기 때문에 최소공배수를 구할때 모두 곱해주면 된다.

lcm(1,2,3,5,7,11,13,17,19) -> 즉 223,092,870번 이후 같은 패턴이 반복되게 된다.



같은 10줄의 코드라도 소수를 활용하였을때 훨씬 효율적이라는 것을 알 수 있다.


전체 코드

<!DOCTYPE html>
<html>
<head>
<title>css 랜덤 예제</title>
<meta charset="UTF-8" />
<style>
body {
background-color: lightgray;
}
.content {
float: left;
width: 10px;
height: 10px;
}
.content:nth-child(n) {
background-color: red;
}
.content:nth-child(2n) {
background-color: green;
}
.content:nth-child(3n) {
background-color: blue;
}
.content:nth-child(5n) {
background-color: black;
}
.content:nth-child(7n) {
background-color: orange;
}
.content:nth-child(11n) {
background-color: violet;
}
.content:nth-child(13n) {
background-color: cyan;
}
.content:nth-child(17n) {
background-color: yellow;
}
.content:nth-child(19n) {
background-color: gray;
}
.content:nth-child(23n) {
background-color: white;
}
</style>
</head>
<body>
<div class="container"></div>
</body>
<script>
const container = document.querySelector('.container');

for (let i = 0; i < 1000; i++) {
const content = document.createElement('div');
content.classList.add('content');
container.appendChild(content);
}
</script>
</html>



실행 결과

Live Editor
function App() {
  const html = `
  <!DOCTYPE html>
  <html>
    <head>
      <title>css 랜덤 예제</title>
      <meta charset="UTF-8" />
      <style>
        body {
          background-color: lightgray;
        }
        .content {
          float: left;
          width: 11px;
          height: 11px;
        }
        .content:nth-child(n) {
          background-color: red;
        }
        .content:nth-child(2n) {
          background-color: green;
        }
        .content:nth-child(3n) {
          background-color: blue;
        }
        .content:nth-child(5n) {
          background-color: black;
        }
        .content:nth-child(7n) {
          background-color: orange;
        }
        .content:nth-child(11n) {
          background-color: violet;
        }
        .content:nth-child(13n) {
          background-color: cyan;
        }
        .content:nth-child(17n) {
          background-color: yellow;
        }
        .content:nth-child(19n) {
          background-color: gray;
        }
        .content:nth-child(23n) {
          background-color: white;
        }
      </style>
    </head>
    <body>
      <div class="container"></div>
    </body>
    <script>
      const container = document.querySelector(".container");

      for (let i = 0; i < 4018; i++) {
        const content = document.createElement("div");
        content.classList.add("content");
        container.appendChild(content);
      }
    </script>
  </html>
`;

  return (
    <div>
      <iframe srcDoc={html} style={{ width: '100%', height: '35em' }}></iframe>
    </div>
  );
}
Result
Loading...


참고자료