Как выбрать исключительно первые три элемента (ребёнка) у родительского элемента?

Задача состоит в том, чтобы выбрать исключительно первые три элемента у родителя, конечно же можно написать так: div > :nth-child(1), div > :nth-child(2), div > :nth-child(3), но мы ведь не ищем лёгких путей, так? (Мой ответ ищите ниже, в секции ответов)

Условия:

  • Нужно сделать так, чтобы первые три элемента у родителя имели красный задний фон
  • Никакого JavaScript, только CSS, только хардкор (тестирование селекторов разрешено, но получение элементов с помощью JavaScript нет)
  • Не нарушаем принципы DRY (Конкретней, не нарушать их в CSS селекторах)
  • Ответ обязан быть универсальным и должен работать не только на этом примере, но и на тех пример, где элементов может быть в разы больше.

Вот пример кода для ваших ответов:

div > YOUR_SELECTOR {
  background-color: red;
}
<div>
  <p>Этот элемент должен иметь красный задний фон</p>
  <p>Этот элемент должен иметь красный задний фон</p>
  <p>Этот элемент должен иметь красный задний фон</p>
  <p>У этого элемента не должно быть красного заднего фона</p>
  <p>У этого элемента не должно быть красного заднего фона</p>
  <p>У этого элемента не должно быть красного заднего фона</p>
  <p>У этого элемента не должно быть красного заднего фона</p>
  <p>У этого элемента не должно быть красного заднего фона</p>
</div>


Ответы (1 шт):

Автор решения: ΝNL993

Решения

Все элементы до третьего:

div > :nth-child(-1n+3)

Все элементы которые НЕ 4-ые и выше:

div > :not(:nth-child(n+4))

Также, у наших коллег на Хабре есть решение задачи, но какое-то странное:

div > :nth-child(-1n+3):nth-child(-n+8)

И ещё один ответ, чисто технически он не нарушает никакой из условий, но его применение довольно нежелательно, возможны серьёзные проблемы с совместимостью:

div > :where(:nth-child(1), :nth-child(2), :nth-child(3))

Тестирование

Проведём также тесты всех селекторов на работоспособность, будем проверять по 1000 раз со случайным кол-во'м элементов (минимум 3, максимум 1000). Для примера также добавил неправильный селектор.

let selectors = ['div > :nth-child(-1n+3)', 'div > :not(:nth-child(n+4))', 'div > :nth-child(-1n+3):nth-child(-n+8)', 'div > :where(:nth-child(1), :nth-child(2), :nth-child(3))', 'div > *']

function test(selector) {
  for (let i = 0; i < 1000; i++) {
    let elementsAmount = Math.floor(Math.random() * 1000) + 3
    let parent = document.createElement('div')

    for (let j = 0; j < elementsAmount; j++) {
      let element = document.createElement('p')
      element.dataset.isCorrect = j < 3 ? 1 : 0
      parent.appendChild(element)
    }

    try {
      let queried = parent.querySelectorAll(selector)

      if (queried.length < 4) {
        return true
      }

      return false
    } catch(_) {
      return false
    }
  }
}

function log(selector) {
  console.log(selector, '\nPASSED?', test(selector))
}

for (let i = 0; i < selectors.length; i++) {
  let selector = selectors[i]

  log(selector)
}

P.S. Я знаю что в одном случае из 997 последний "неправильный" вариант сработает.

Ссылки

→ Ссылка