Оптимизация списка селекторов запросов добавление класса

Привет, ребята, я написал следующий код:

 function flyoutFirstHeadline() {
    document.querySelectorAll('.category-top-navigation__item--column-1 .category-top-navigation__item__headline')[0].classList.add('category-top-navigation__item__headline--first');
    document.querySelectorAll('.category-top-navigation__item--column-2 .category-top-navigation__item__headline')[0].classList.add('category-top-navigation__item__headline--first');
    document.querySelectorAll('.category-top-navigation__item--column-3 .category-top-navigation__item__headline')[0].classList.add('category-top-navigation__item__headline--first');
 }

По сути, я просто добавляю некоторый класс к первому вхождению .category-top-navigation__item__headline для каждого столбца, который у меня есть в макете. У меня максимум 3 столбца, начиная с 1.

Я думаю о способах оптимизации и улучшения кода.

Я думаю, что, возможно, лучше выполнить цикл, который проходит по номерам столбцов категории и увеличивается на 1 каждый раз, пока не достигнет 3. Или, может быть, есть какой-то лучший способ сделать это?

С точки зрения чистой производительности, я думаю, что ваш подход самый быстрый. Вы можете использовать querySelector вместо querySelectorAll. Я лично также считаю, что это читаемый код

Stefan van de Vooren 10.09.2018 10:37

Добавляя к тому, что сказал @Stefan, я бы сказал, что querySelector () на самом деле быстрее, потому что он соответствует Только первому такому элементу, а не соответствует всему и передает вам первый, как это делает querySelectorAll () [0].

BoltClock 10.09.2018 10:55
1
2
118
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вы можете выбрать имя класса, которое начинается с определенной подстроки, с помощью строки селектора [class^=someStartingSubStr"]. Итак, вы можете выбрать и изменить сразу все элементы, которые хотите:

function flyoutFirstHeadline() {
  Array.prototype.forEach.call(
    document.querySelectorAll(
      '[class^="category-top-navigation__item--column-"] .category-top-navigation__item__headline'
    ), (headline) => {
      headline.classList.add('category-top-navigation__item__headline--first');
    }
  );
}

Демо:

function flyoutFirstHeadline() {
  Array.prototype.forEach.call(
    document.querySelectorAll('[class^="column-"] .headline'),
    (headline) => {
      headline.classList.add('highlight');
    }
  );
}
flyoutFirstHeadline();
.highlight {
  background-color: yellow;
}
<div class="column-1">
  <div class="headline">headline</div>
</div>
<div class="column-2">
  <div class="headline">headline</div>
</div>
<div class="column-3">
  <div class="headline">headline</div>
</div>

Также обратите внимание, что если вы хотите выбрать только элемент первое, который соответствует селектору (как вы делали с исходным кодом), вы должны использовать querySelector, а не querySelectorAll плюс [0].

будьте осторожны с nodeList.forEach. Internet Explorer не поддерживает это: developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach

Stefan van de Vooren 10.09.2018 10:32
Ответ принят как подходящий

Почему бы не получить элементы заголовка и не назначить класс? Кажется, нет необходимости включать и класс столбца.

function flyoutFirstHeadline() {
  const
    headLines = document.querySelectorAll('.category-top-navigation__item__headline:first-child');
    
  for(let index=0; index < headLines.length; index++) {
    headLines[index].classList.add('category-top-navigation__item__headline--first');
  }
}


flyoutFirstHeadline();
.category-top-navigation__item__headline--first {
  color: blue;
}
<div class=".category-top-navigation__item--column-1">
  <h1 class="category-top-navigation__item__headline">Col 1 / headline 1</h1>
  <h2 class="category-top-navigation__item__headline">Col 1 / headline 2</h2>
</div>

<div class=".category-top-navigation__item--column-2">
  <h1 class="category-top-navigation__item__headline">Col 2 / headline 1</h1>
  <h2 class="category-top-navigation__item__headline">Col 2 / headline 2</h2>
</div>

<div class=".category-top-navigation__item--column-3">
  <h1 class="category-top-navigation__item__headline">Col 3 / headline 1</h1>
  <h2 class="category-top-navigation__item__headline">Col 3 / headline 2</h2>
</div>

Я обновил свой ответ. Он применяет класс к первому элементу заголовка в столбце и делает его текст синим.

Некоторые столбцы имеют более одного заголовка, но мне нужен только дополнительный класс для первого заголовка.

Mikkel Fennefoss 10.09.2018 11:26

Вы бы знали, как добавить эту функциональность в свой код?

Mikkel Fennefoss 10.09.2018 11:34

Я обновил свой ответ, думаю, теперь он делает то, что вы ищете.

Thijs 10.09.2018 11:40

Решение, ориентированное на ясность и гибкость:

  1. нет магических ценностей
  2. используйте for..of для итераций
  3. часто используемые функции с коротким именем
  4. гибкий элемент соответствует селектору атрибутов
  5. длинные строки, разделенные на простые в управлении куски

function flyoutFirstHeadline() {
  const firstHeadlineSelector = [
    '[class^="category-top-navigation__item--column-"]',
    '.category-top-navigation__item__headline:first-child'
  ].join(' ');

  const newClass = 'category-top-navigation__item__headline--first';

  for (const el of $$(firstHeadlineSelector)) {
    el.classList.add(newClass);
  }
}


// helpers

const $$ = document.querySelectorAll.bind(document);

flyoutFirstHeadline()
[class^="category-top-navigation__item--column-"] {
  margin-bottom: 1rem;
}

.category-top-navigation__item__headline--first {
  color: red;
}
<div class="category-top-navigation__item--column-1">category 1
  <div class="category-top-navigation__item__headline">headline 1</div>
  <div class="category-top-navigation__item__headline">headline 2</div>
</div>
<div class="category-top-navigation__item--column-2">category 2
  <div class="category-top-navigation__item__headline">headline 1</div>
  <div class="category-top-navigation__item__headline">headline 2</div>
</div>
<div class="category-top-navigation__item--column-3">category 3
  <div class="category-top-navigation__item__headline">headline 1</div>
  <div class="category-top-navigation__item__headline">headline 2</div>
</div>

Назначьте className каждому тегу в верхней части каждого столбца

Изменения

HTML макет

Я догадался, как у вас макет:

  • каждый столбец - <section class='col'>

  • в каждом столбце есть два <h2 class='headline'>, один вверху и один внизу.


className в .classList

Я также сократил classNames в .classList, чтобы сохранить рассудок:

.category-top-navigation__item--column- === .col === <section class="col">

.category-top-navigation__item__headline === .headline === <h2 class="headline">

.category-top-navigation__item__headline--first === .first === <h2 class="headline first">

✱=1,2,3 ex. .col2


Решения

CSS Псевдокласс :first-of-type

Применительно к селектору section h2.headline:first-of-type он работает следующим образом:

Find all <h2 class="headline"> that are the :first-of- its type (i.e.type: <h2>) within its parent element: <section>.


Сбор тегов JavaScript в NodeList

Для использования ранее упомянутого селектора идеально подходит document.querySelectorAll("раздел h2.headline: first-of-type"). Результатом является array-like Object (или NodeList), конкретно состоящий из трех <h2 class="headline">, расположенных как первый <h2> в его собственном столбце <section>.

При работе с несколькими элементами, числами, строками, объектами и т. д. Вам необходимо выполнить итерацию (или цикл) по каждому члену этой группы (т.е. NodeList, HTMLCollection, массив, простой объект, объект, подобный массиву, и т. д.):

first - это массив Object / NodeList:

var first = document.querySelectorAll("section h2.headline:first-of-type");


Итерировать (цикл) через NodeList с помощью цикла for()

Чтобы получить доступ к каждому <h2> в first, вы должны пройти через first:

for (let i=0; i < first.length; i++) {...
  • для каждого цикла через first: for(...
  • начать счетчик циклов с нуля: let i = 0;
  • остановка при достижении общего количества петель: i < first.length; // 3
  • увеличить счетчик циклов на 1: i++) {...

В каждой итерации цикла for()className: .first добавляется к <h2 class="headline">:

first[i].classList.add('first')

.first { text-decoration: underline }


Демо

В случае успеха три <h2>, расположенные вверху своих столбцов <section>, будут подчеркнуты.

function firstHeadline() {
  var first = document.querySelectorAll('section h2.headline:first-of-type');
  for (let i = 0; i < first.length; i++) {
    first[i].classList.add('first');
  }
}

// Optional
function dateTime(time) {
  var node = document.querySelector(time);
  var option = {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  };
  var D = new Date().toLocaleDateString("en-US", option);
  node.setAttribute('date-time', D);
  node.textContent = D;
}

dateTime('time');

firstHeadline();
html,
body {
  font: 500 16px/1.2 Times;
  height: 100%;
  width: 100%;
}

h1 {
  line-height: 2;
  position: absolute;
  left: 50%;
  top: 5%;
  transform: translate(-50%, -75%);
}

h1,
h2,
sub {
  text-align: center;
  display: block;
}

article {
  display: flex;
  justify-content: space-around;
  width: 98%;
  height: auto;
}

section {
  width: 32%;
  min-height: 100%;
  border: 0.5px inset rgba(51, 51, 51, 0.15);
  padding: 3px;
}

p {
  margin: 3px;
  text-align: justify;
  font-size: .75rem;
}

header {
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
}

header,
footer {
  height: 10%;
  width: 96%;
}

.headline,
h1 {
  letter-spacing: 1.5px
}

time {
  font-size: 0.85rem;
}

.first {
  text-decoration: underline;
}
<header>
  <h1>Main Title</h1>
  <time datetime=""></time>
</header>
<hr>
<article>
  <section class='col1'>
    <h2 class='headline'>Headline 1A</h2>
    <p>Cras pellentesque tempor ex, vitae pulvinar risus lobortis nec. Sed quis aliquet nulla. Quisque egestas lectus et facilisis sodales.</p>
    <sub>🙑</sub>
    <h2 class='headline'>Headline 1B</h2>
    <p>Maecenas elementum efficitur nibh pretium pellentesque. Cras vitae ipsum nisi. Nam sapien justo, faucibus et metus sed, vestibulum bibendum eros. Nunc luctus gravida ex, vel feugiat magna porta ut. Nam et pretium risus.</p>
    <sub>🙑</sub>
  </section>
  <section class='col2'>
    <h2 class='headline'>Headline 2A</h2>
    <p>Sed et facilisis nulla, non pulvinar nisl. Duis ultricies nisi luctus sem aliquam ornare. Curabitur rutrum lorem vitae turpis eleifend, sed dignissim nisi ultricies. Sed efficitur interdum est.</p>
    <sub>🙑</sub>
    <h2 class='headline'>Headline 2B</h2>
    <p>Pellentesque mollis viverra orci et blandit. Proin a sem fermentum magna vehicula tinidunt. Proin tempor orci ut ullamcorper tristique.</p>
    <sub>🙑</sub>
  </section>
  <section class='col3'>
    <h2 class='headline'>Headline 3A</h2>
    <p>Suspendisse ultricies, sapien nec elementum lacinia, quam enim maximus lorem, eu hendrerit nisi libero non velit. Nam eget mattis turpis, non lacinia ante. Fusce viverra sem sit amet sodales vulputate.</p>
    <sub>🙑</sub>
    <h2 class='headline'>Headline 3B</h2>
    <p>Duis nisi nibh, ultrices sit amet enim ac, scelerisque dictum justo. Morbi tempor ante sit amet ante maximus consectetur.</p>
    <sub>🙑</sub>
  </section>
</article>
<hr>
<footer>
  <address></address>
</footer>

Когда речь идет о выступлении, то, что у вас есть, уже хорошо. Хотя, как сказал @BoltClock в комментариях, .querySelector(...) вместо .querySelectorAll(...)[0] было бы еще лучше, он просто выбирает первый элемент.

Если вы ищете "красивее" ..

document
.querySelectorAll('.category-top-navigation__item__headline:first-child')
.forEach(el => el.classList.add('category-top-navigation__item__headline--first'))

И даже красивее, возможно, даже с лучшей читабельностью.

const headline = 'category-top-navigation__item__headline'

document.querySelectorAll(`${headline}:first-child`)
.forEach(el => el.classList.add(`${headline}--first`))

Обратите внимание, что в приведенном выше ответе используется трюк first-child от Ответ Тиджса.

Другие вопросы по теме