AutoclassCSS — быстрый каркас на основе HTML

    image
    Генератор будет полезен для тех, кто верстает статические страницы.
    Инструмент написан на JavaScript, и, следуя методологии БЭМ, каркас формируется только на основе классов.
    Работает просто: на вход получает HTML, на выходе отдаёт CSS.

    Попробовать в действии можно на демонстрационной страничке, оформление настраивается по вкусу.


    Иногда приходится по-быстрому сверстать страничку. Обычно у меня процесс состоит из следующих этапов:
    1. пишу HTML (в уме продумываю для него CSS)
    2. копирую все классы из написанного HTML в CSS-файл
    3. пишу CSS-стили

    Второй пункт хочется исключить, потому что это механическая работа. Автоматизируй!

    Кроме использования в онлайне, скриптом можно воспользоваться прямо на собственной страничке.

    Для этого достаточно подключить один файл.
    <script src="autoclasscss.js"></script>
    

    При создании экземпляра AutoclassCSS можно сразу передать HTML.
    var autoclass = new Autoclasscss('<div class"block">...</div>');

    Или в последующем задать/поменять обрабатываемый HTML.
    var autoclass = new Autoclasscss();
    autoclass.set('<div class"block">...</div>');
    

    Для получения результирующей строки достаточно воспользоваться методом get.
    var css = autoclass.get();
    

    Кроме того, можно настроить внешний вид конечного результата. Для этого предусмотрен ряд методов.
    autoclass
        .brace('newline')             // Способ отображения открывающей скобки, по умолчанию — через пробел после селектора
        .flat(true)                   // Плоский список селекторов, не устанавливать отступы по иерархии вложенности
        .ignore(['class1', 'class2']) // Добавление игнорируемых классов
        .indent('tabs')               // Устанавливать отступы табами, по умолчанию — четырьмя пробелами
        .inner(false)                 // Не добавлять отступ внутри фигурных скобок
        .line(true)                   // Отбивать селекторы пустой строкой
        .tag(true);                   // Указывать теги
    


    Все методы удобно записываются цепочкой, более подробная документация собрана на основе JSDoc.
    Поделиться публикацией

    Комментарии 58

      +1
      FYI: Есть похожая разработка: grunt-init-block для Grunt.
        0
        Спасибо, я не знал о ней.
        Интересна идея вытаскивать из html конкретный блок.
        Жаль только, что вложенность тегов не повторяется.
          0
          Я автор этого грант-плагина. Если есть вопросы, буду рад :)
          Жаль только, что вложенность тегов не повторяется.
          Обычно, когда верстаю, элементы складываю на один уровень вложенности. Если нужен второй, создаю новый блок. Делал плагин исходя из такой иерархии.
            0
            Да, я так и понял. Это чисто БЭМ-подход.
            Мне хотелось сделать инструмент, который не будет полностью завязан на методологии, для большей свободы.
        +1
        Добавьте возможность использования регелурных выражений в ignore, т.к. в большинстве случаев, просто используется префикс:

        <div class="foo__bar_baz js-foo__bar_baz" />
        


        И еще, сделайте опции в виде хеша, а не методов.
          0
          Добавил issue про ignore.
          Сначала я тоже хотел сделать опции в виде хеша, но потом передумал в пользу отдельных методов, поскольку это выразительнее и гибче.
            0
            Опции всегда должны описываться в декларативном стиле.
            Сначала я тоже хотел сделать опции в виде хеша, но потом передумал в пользу отдельных методов, поскольку это выразительнее и гибче

            Насчет гибкости не понял. За счет чего она должна достигатья?
            Как-правило, настройки все хранят в отдельном файле, в вашем же случае без «магии» не обойтись.

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

            # config.yaml
            
            brace:  ['newline']
            ignore: ['js-.*']
            flat:      true
            indent: 'tabs'
            inner:   true
            line:     true
            
            

            // project1.js
            
            var options = Object.mixin({
              tag: 'true',
              ignore: ['foo']
            }, options);
            
            new Autoclasscss(options);
            
            // project2.js
            
            var options = Object.mixin({
              ignore: ['bar', 'js-*']
            }, options);
            
            new Autoclasscss(options);
            


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

              Можно сделать обёртку, которая будет возвращать экземпляр со стандартными настройками.
              return (function() {
                  return new Autoclasscss().tag(true).flat(false);
              })();
              

              И на проектах использовать её, переопределяя необходимые опции.
              require('autoclasscss').tag(false).set('<html>').get();
              
                0
                Все-таки опции должны оставаться оставаться опциями, а не методами.

                Представтьте что, кто-то захочет добавить ваш плагин в грант.
                  0
                  Убедили, добавил issue.
          +1
          Зачем вы используете window?

          var __global__ = typeof global !== 'undefined' ? global : function (object) {
              return object;
          }(this);
          
            0
            (0,eval)('this')
              0
              Тогда уж:

              Function ('return this')();
              

              Но зачем?
                0
                Чтобы гарантированно получить глобальный контекст + минимум кода.
                  0
                  eval в первую очередь зло.
                  А минимум кода должено должно достигаться за счет сжатия
                    0
                    1. Не будьте так категоричны
                    2. Function(/*body*/) — это форма eval
                    3. Сжимальщику иногда нужно помогать.
                      0
                      1. Не будьте так категоричны

                      Просто так не нужно писать, тем более для таких простых вещей как получение ссылки на глобальный объект.

                      2. Function(/*body*/) — это форма eval

                      Именно по этому я ее и привел, т.к. это почти единственная форма динамического исполнения кода, которая допустима в строгом режиме.

                      3. Сжимальщику иногда нужно помогать.

                      Вот что должно помогать developers.google.com/closure/compiler/docs/js-for-compiler
                        0
                        Почему нельзя просто?
                        (function(global) {

                        })(this);
                          0
                          Потому что так не получится, если используется AMD.
                            0
                            Да, но у меня не используется AMD.
                            А если надо применить этот скрипт в AMD, то его достаточно вызвать в нужном контексте.
                              0
                              Ага, понимание того как и где это может испольваться приходит не сразу!
                              0
                              Конечно можно, только так:

                              (function(global) {
                                //..
                              }).call(this);
                              


                              Все зависит от проекта, кому как удобно.
                              Да и замыкания не бесплатны, особенно в больших проектах, т.к. они меннее эффективно развоваричаются.
                                0
                                Только то, что вы написали — неправильно. global у вас будет undefined.
                                  0
                                  Я хотел показать что в таком случае this будет ссылаться на глобальный объект;
                                  Хотя конечно, лучше оставить global, чем передавать два объекта
                                    0
                                    Не будет
                                      0
                                      'use strict';
                                      
                                      (function () {
                                        alert(this);
                                      }).call(this);
                                      


                                        0
                                        Я не понимаю, что вы мне пытаетесь доказать?
                                        !function () { 'use strict';
                                        
                                        (function () {
                                          alert(this);
                                        }).call(this); }()
                                        
                                          0
                                          Вы первый начали это троллинг.

                                          Просто я действительно не понимаю зачем использовать eval, если можно использовать конструктор Function (пусть даже это тоже eval)?
                                            0
                                            Хотя я конечно согласен, что для библиотеки, которая будет использоваться «не пойми как», без «eval» для получения ссылки на глобальный объект не обойтись.
                              0
                              Просто так не нужно писать, тем более для таких простых вещей как получение ссылки на глобальный объект.
                              Расскажите этим ребятам github.com/knockout/knockout/blob/master/build/fragments/extern-pre.js#L4

                              единственная форма динамического исполнения кода, которая допустима в строгом режиме.
                              Вот врать-то зачем? eval прекрасно работает в strict mode.
                                –1
                                Вот врать-то зачем? eval прекрасно работает в strict mode.

                                The eval code cannot instantiate variable or function bindings in the variable environment of the calling context that invoked the eval if either the code of the calling context or the eval code is strict code. Instead such bindings are instantiated in a new VariableEnvironment that is only accessible to the eval code.

                                Такая конструкция?
                                (0,eval)('this')
                                

                                А вот такое заработает?
                                (function eval () { 
                                  'use strict'; 
                                    alert(this);
                                })();
                                
                                  0
                                  1. В strict mode eval сделали безопаснее, об этом и речь в приведенной цитате. Т.е. все что происходит внутри eval — остается внутри eval.

                                  2. Причем тут вообще второй пример? Он не будет работать, потому что в strict mode нельзя переопределять eval и arguments.

                                  Вот, вам чтиво на вечер sriku.org/blog/2012/08/28/on-eval-being-evil/
                                    0
                                    Не нужно мне кидать всякие левые сслылки, в спецификации все и так подробно написано.

                                      –1
                                      Вам нужно провериться у психоаналитика, у вас походу эгоцентризм.
                                    0
                                    Ну или менее экзотическое:

                                    (function() {
                                        'use strict';
                                    
                                        eval('var x = 1;');
                                    
                                        alert(x);  // Uncaught ReferenceError: x is not defined 
                                    }());
                                    
                                      0
                                      Все, правильно — eval стал безопаснее, и не засоряет вызывающий контекст лишними переменными.

                                      Хотите объявлять переменные в родительском контексте, делайте так: this.x = 1; никогда не делайте так, и так, как в примере выше.

                                      (function() {
                                          'use strict';
                                      
                                          eval('this.x = 1;');
                                      
                                          alert(x); // 1
                                      }.call(this)); //нужно, т.к. иначе в strict mode this будет undefined.
                                      
                                        +1
                                        И, кстати,
                                        Function('var x = 1;')()
                                        
                                        Всегда работает аналогично eval'у в strict mode.
                                          0
                                          (function () {
                                              'use strict';
                                              alert(Function('return this')());
                                          })();
                                          

                                          отсюда.
                                          по-моему самый «человечный» вариант, как на мой взгляд, нежели запятые и eval:
                                          Functions that are created with the Function constructor are strict only if they start with a Use Strict Directive, they don't «inherit» the strictness of the current context as Function Declarations or Function Expressions do

                                            0
                                            Я имел ввиду не полностью аналогично, а примерно так:

                                            "Function, в отношении объявления переменных всегда работает аналогично eval'у в strict mode."
                                              0
                                              За одним лишь исключением — код подаваемый в конструктор Function, в отличии от eval может быть оптимизирован (в V8 точно).
                                                0
                                                Что можно оптимизировать в 'this' или 'return this'?
                                                  0
                                                  Да это просто к слову :)
                        0
                        Потому что рассчитываю на использование только в браузере.
                        Как вы думаете, нужен ли CLI для этой штуки?
                          0
                          А зачем это в браузере?
                            0
                            Ну, чтобы можно было написать html, открыть в браузере страничку с визуальным интерфейсом и скопировать от туда результирующий css.
                              0
                              Можно настроить свой редактор кода, проксю, прикрутить мониторинг на изменение файла, но никто не будет открывать браузер и копировать туда разметку чтобы получить CSS.
                      0
                      А можно не перезаписывать CSS-файл?
                        0
                        Не понял вопрос.
                          0
                          Если настрить вывод в определенный файл, в котором уже есть стили, то они перезапишутся.
                            0
                            Вы же сами настраиваете вывод, сделайте так, чтобы он добавлялся в конец :)
                              0
                              А на сколько сложно будет сделать чтобы правила добавлялись в нужном семантическом порядке?

                                0
                                Речь идёт о сортировке css-свойств?
                                Для этого есть классная штука, csscomb.
                                  0
                                  Представьте что у меня есть такая разметка:

                                  <header class="page">
                                      <span class="page__element">
                                  </header>
                                  


                                  CSS-код, который получился:

                                  .page {
                                      
                                  }
                                      .page__element {
                                          
                                      }
                                  


                                  Затем я добавтл стили:

                                  .page {
                                        width: 100px;
                                  }
                                      .page__element {
                                            color: red;
                                      }
                                  
                                  


                                  И через какое-то время, я решил добавить еще один модификатор в разметку:

                                  <header class="page">
                                      <span class="page__element page__element_gren">
                                  </header>
                                  


                                  И ожидаю что в стилях будет следующее:

                                  .page {
                                         width: 100px;   
                                  }
                                      .page__element {
                                          color: red;
                                      }
                                          .page__element_gren {
                                  
                                          }
                                  


                                  А на деле:

                                  .page {
                                      
                                  }
                                      .page__element {
                                          
                                      }
                                      .page__element_gren {
                                          
                                      }
                                  


                                    0
                                    Понял идею. Это конечно круто, но тяжело будет сделать.
                                    Если что, могу рассмотреть пулреквест :)
                        0
                        возможно кому-то пригодится github.com/generalov/bem-reverse

                        split-css.js — раскладывает блочный css на отдельные файлы для элементов и модификаторов внутри одного уровня переопределения. полезно для быстрого прототипирования.
                        reverse-html.js — строит bemjson по html с классами в bem-стиле
                        reverse-css.js — распиливает скомпилированную css, с учетом уровней переопределения.

                        писалось, для о-bem-ливания верстки прототипов и изучения логики bem-tools на примере реальных яндексных сайтов. кстати, благодаря БЭМ код у них получается хорошо отделимым. =)
                          0
                          неплохо бы это все в ридми добавить :)
                          0
                          Спрашивал у Хабр жителей такое решение.
                          Вот что советовали:
                          javascript:var g=[];for(var i in document.all) if(document.all.hasOwnProperty(i) && document.all[i].id) g.push('#'+document.all[i].id); alert(g.join("\n")); автор CAH4A

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

                          Самое читаемое