Ускоряем верстку по БЭМ

1078
views

В данной статье я расскажу, как автоматизировать процесс построения дерева классов с вложенностью в вашем sass файле, для верстки документа построенного по методологии БЭМ.

Прежде всего, что такое БЭМ?

Независимость sass блоков по БЭМ

БЭМ — методология для фронт-енда от Яндекса. Суть которой улучшить понимание верстки между разработчиками проекта, ускорить процесс разработки и повысить качество фронт-енда за счет независимости блоков друг от друга.

Главная идея заключается в том, что у каждого элемента есть свой главный класс, который указывает на саму сущность элемента, а так же есть классы модификаторы, которые как-то видоизменяют конкретные элементы.

Итоговая верстка выглядит примерно так:

<!DOCTYPE html>
<html lang="ru">
  <head>
  <!-- head content -->
  </head>
  <body>
    <header class="header"></header>
    <div class="wrapper">
      <div class="base">
        <ul class="article_list">
          <li class="article_item first_item">
            <div class="article_block">
              <h2 class="article_title red_mod">Some tile</h2>
              <div class="article_def">
                <p>some text about article</p>
              </div>
            </div>
          </li>
          <li class="article_item last_item">
            <div class="article_block">
              <h2 class="article_title green_mod">Some tile</h2>
              <div class="article_def">
                <p>some text about article</p>
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
    <footer class="footer"></footer>
  </body>
</html>

Как мы видим, у каждого элемента есть свой класс, а у элементов <li> и h2 есть еще классы модификаторы.

Как же можно ускорить верстку такого шаблона?

Идея заключается в автоматизации написания классов sass и их структуры. А именно js-функции, которая бы могла обработать всю html страничку и выдать готовый sass файл с деревом классов по БЭМу.

Собственно сама функция:

function getAllClasses(context, output) {
  var finalArray = [],
    mainArray = [],
    allElements = $(context).find($('*'));//найдем все элементы документа
  //если у элемента есть класс - добавим в массив
  for (var i = 0; i < allElements.length; i++) {
    var someElement = allElements[i],
      elementClass = someElement.className;
    if (elementClass.length > 0) {//если есть классы
      //если содержит несколько классов - разделим их
      var elementClassArray = elementClass.split(' '),
        classesAmount = elementClassArray.length;
      if (classesAmount === 1) {
        mainArray.push('.' + elementClassArray[0] + ' {');
      } else {
        var cascad = '.' + elementClassArray[0] + ' {';
        for (var j = 1; j < elementClassArray.length; j++) {
          cascad += ' &.' + elementClassArray[j] + ' { }';
        }
        mainArray.push(cascad);
      }
    }
  }

  //создание финального массива без повторяющихся элементов
  var noRepeatingArray = unique(mainArray);
  noRepeatingArray.forEach(function (item) {
    var has = false;
    var preWords = item.split('&');
    for (var i = 0; i < finalArray.length; ++i) {
      var newWords = finalArray[i].split('&');
      if (newWords[0] == preWords[0]) {
        has = true;
        for (var j = 0; j < preWords.length; ++j) {
          if (newWords.indexOf(preWords[j]) < 0) {
            newWords.push(preWords[j]);
          }
        }
        finalArray[i] = newWords.join('&');
      }
    }
    if (!has) {
      finalArray.push(item);
    }
  });
  for (var i = 0; i < finalArray.length; i++) {
    $('<div>' + finalArray[i] + ' }</div>').appendTo(output);
  }


  //Функция удаляющая повторяющиеся элементы из массива
  function unique(A) {
    var n = A.length, k = 0, B = [];
    for (var i = 0; i < n; i++) {
      var j = 0;
      while (j < k && B[j] !== A[i]) j++;
      if (j == k) B[k++] = A[i];
    }
    return B;
  }
}

В качестве первого параметра мы указываем откуда собрать классы, во втором — куда вывести результат.

Теперь добавим в наш документ элемент в который будут выводится классы для будущего sass файла:

<!DOCTYPE html>
<html lang="ru">
  <head>
  <!-- head content -->
  </head>
  <body>
    <header class="header"></header>
    <div class="wrapper">
      <div class="base">
        <ul class="article_list">
          <li class="article_item first_item">
            <div class="article_block">
              <h2 class="article_title red_mod">Some tile</h2>
              <div class="article_def">
                <p>some text about article</p>
              </div>
            </div>
          </li>
          <li class="article_item last_item">
            <div class="article_block">
              <h2 class="article_title green_mod">Some tile</h2>
              <div class="article_def">
                <p>some text about article</p>
              </div>
            </div>
          </li>
        </ul>
        <div class="elements_list"></div>
      </div>
    </div>
    <footer class="footer"></footer>
  </body>
</html>

Теперь осталось только вызвать jQuery функцию (в примере поиск по классам осуществляется с помощью jQuery, но можно искать с помощью document.querySelectorAll(‘*’) )

$(document).ready(function ($) {
  getAllClasses('html','.elements_list');
});

Функция соберет все классы в документе и выведет их в элемент с классом ‘elements_list’. Результат:

.header { }
.wrapper { }
.base { }
.article_list { }
.article_item { }
.article_block { }
.article_title { &.red_mod { }&.green_mod { } }
.article_def { }
.article_title { }
.article_def { &.big_mod { } }
.elements_list { }
.footer { }

Теперь вставив полученный код в sass файл и выполнив автоформатирование мы получим готовое дерево:

.header {
}

.wrapper {
}

.base {
}

.article_list {
}

.article_item {
}

.article_block {
}

.article_title {
  &.red_mod {
  }
  &.green_mod {
  }
}

.article_def {
}

.article_title {
}

.article_def {
  &.big_mod {
  }
}

.elements_list {
}

.footer {
}

Обратите внимание, что классы-модификаторы автоматически подставляются внутри основных классов.