Хочу поделиться своей небольшой библиотекой. Интересно услышать ваше мнение о ней.

Сразу к сути дела: htmlmake — это js-функция, позволяющая создавать строку с html-разметкой внутри для дальнейшей вставки в DOM-дерево.

Зачем это использовать?


Начну немного издалека. Современную веб-разработку я бы условно поделил на 2 категории:
  • Сайты, в которых html генерируется сервером, а javascript используется в основном для анимаций и ajax запросов (далее их я так и буду называть «сайты»);
  • Одностраничные приложения, в которых js берет на себя всю отрисовку DOM (далее «веб-приложения»).

Если говорить о веб-приложениях, то в них логично распространение js-шаблонизаторов для генерации html (например Jade). Моя библиотека рассчитана на 1 группу, в которой чаще всего распространен компонентный подход к написанию js-ов. Когда внедрять js-шаблонизатор ради генерации несложного набора html элементов слишком дорого, обычно html генерируется своим jquery кодом. Допустим, нам необходимо собрать следующий html:

 <div class='wrapper'><h1>Привет, Хабр!</h1></div>

Тогда обычно мы пишем как-то так:

$(“<div>”).addClass(“wrapper”).append($(<h1>).html(“Привет, Хабр!”))

Или вот так:

$(“<div class='wrapper'><h1>Привет, Хабр!</h1></div>”)

И, на мой взгляд, такой код трудно читать и поддерживать. Вот пример использования моей функции:

htmlString = htmlmake ->
  @div "wrapper", ->
    @h1 "Привет, Хабр!"

Почему все примеры на coffeescript?


Вложенные функции пишутся слишком длинно на нативном js, поэтому сразу же не рекомендую использовать эту функцию тем, кто не использует coffeescript.

Вот пример чуть посложнее:

 html = htmlmake ->
    @div "hello-class", ->
      @ul ->
        @li "one"
        @li "two"
        @li "three"
      @a href: "http://google.com", "underworld!"

Интерфейс задуман максимально легкий. Cразу понятно, какой html будет сгенерирован. Результат:

 <div class='hello-class'>
    <ul>
      <li>one</li>
      <li>two</li>
      <li>three</li>
    </ul>
    <a href='http://google.com'>underworld</a>
  </div>

А если я хочу свой this?


Частенько бывает, что нам необходимо пробрасывать контекст в обработчики. Благо в coffescript это делается минимумом усилий. Но в этом случае мы теряем методы, генерирующие dom элементы, поэтому был предусмотрен входящий параметр во всех callback-ах. Вот пример:

@hello = "superman"
html = htmlmake (hm)=>
  hm.span id: "super", @hello

Результат:

 <span id='super'>superman</span>

Пример посложнее:

@names = ["Katarina", "Diana", "Alistar"]

  html = htmlmake (m)=>
    m.div "names", (m)=>
      m.ul (m)=>
        for name in @names
          m.li name

Результат:

 <div class='names'>
    <ul>
      <li>Katarina</li>
      <li>Diana</li>
      <li>Alistar</li>
    </ul>
  </div>

Спасибо за внимание! Буду рад услышать вашу критику/советы/пожелания.

Ссылка на репозиторий, ну или bower install html-maker.

Комментарии (24)


  1. kuser
    30.04.2015 12:24
    -3

    Джва года ждал такую либу, батя грит малаца, хорошо зделоли.


  1. Stalker_RED
    30.04.2015 13:01

    htmlString = htmlmake ->
      @div "wrapper", ->
        @h1 "Привет, Хабр!"
    А почему в случае с дивом строка стала классом, а в случае h1 содержимым тега? В чем разница?


    1. extempl
      30.04.2015 13:23

      Я так понимаю, если был передан коллбэк, то строка определяется как класс, если нет — то тег «простой» и она определяется как контент.


    1. lxkuz Автор
      30.04.2015 13:33

      Я посчитал, что атрибут class является самым востребованным, поэтому в случае, если есть второй параметр (строка или коллбэк) и первый параметр является строкой, то будем считать, что это класс создаваемого элемента. Подробнее про варианты использования можно подсмотреть в тестах . Первый параметр считаем контентом элемента, если второго параметра нет.


      1. impwx
        30.04.2015 14:31

        Если библиотека основывается на таких допущениях, то имхо читать и поддерживать такой код ничуть не легче, чем $("<div>...</div>").


        1. lxkuz Автор
          30.04.2015 14:37

          Вам никто не мешает писать более строго:

            htmlString = htmlmake ->
              @div class: "wrapper", ->
                @h1 {}, "Привет, Хабр!"
          


          Вложенную структуру так все-равно удобнее читать, чем $("..."). ИМХО, опять же.


          1. impwx
            30.04.2015 14:53

            Если требуется кусок HTML больше чем на 2-3 строчки, есть резон вынести его в отдельный файл и скормить шаблонизатору. Плюсы — не мешаем логику с разметкой и пользуемся работающим autocomplete.


            1. lxkuz Автор
              30.04.2015 15:13

              Согласен. Применение этой библиотеки имеет смысл только когда в готовом js компоненте неожиданно потребовалось добавить совсем немного html. Ну и плюс к этому она весит 40кб в минимизированном виде. Так что добавим еще тех, кто пишет мобильные версии сайтов.


              1. impwx
                30.04.2015 15:32
                +2

                она весит 40кб в минимизированном виде
                Сначала подумал, что у вас опечатка, но проверил в репозитории — так и есть. Вы где-то нахимичили, потому что это безумно много. Для сравнения, Mustache в минифицированном виде весит 9 кб, а Underscore целиком, включая шаблонизатор — 16 кб. И вот еще интересная таблица с размерами.


                1. lxkuz Автор
                  30.04.2015 15:48

                  Какой ужас! А ведь Underscore просто монстр в сравнении с моей скромной функцией) Спасибо за замечание, будем худеть!


                  1. discopalevo
                    30.04.2015 15:56

                    билдфайл шедевральный, с каждым билдом min версия будет становиться в 2 раза больше.


                    1. lxkuz Автор
                      01.05.2015 21:26

                      Спасибо за наводку! Исправлено. Итоговый размер: 4кб


  1. UnknownHero
    30.04.2015 15:19

    «Когда внедрять js-шаблонизатор ради генерации несложного набора html элементов слишком дорого...»

    Даже не знаю, что дороже.


  1. whunter
    30.04.2015 15:20

    <div class='hello-class'>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </div>
    


    Вы пропустили
    @a href: "http://google.com", "underworld!"  
    <a href="http://google.com">underworld!</a>
    


    1. lxkuz Автор
      30.04.2015 15:51

      исправил, благодарю


  1. piumosso
    30.04.2015 15:48

    А чем реакт вам не угодил?) Имхо какая-то фигня)


    1. lxkuz Автор
      30.04.2015 15:53

      Очевидно, не все проекты заказчиков написаны на реакте)


      1. piumosso
        30.04.2015 16:03

        Очевидно)


      1. piumosso
        30.04.2015 16:08

        Реакт просто вспомнился похожим стилем описания (виртуального) дома


  1. nmakarov
    02.05.2015 03:20

    Лет пять назад я такое писал для PHP, тогда было актуально. А сейчас — ReactJS в руки и вперед. Вот, к примеру:

        var name = 'Хабр';
        var html = (
            <div className="wrapper">
                <h1>Привет, {name}</h1>
            </div>
        );
    


    а потом это все прогнать через jsx и всё. Или, все равно же проект собирать придется, так что можно webpack заюзать, у него соответствующий loader имеется.


  1. Louter
    02.05.2015 15:09

    От чего библиотека выиграет в функционале, но может проиграть в размере:
    1) «регистрация» тегов. У вас по-умолчанию мало тегов («div», «ul», «li», «form», «input», «select», «option», «i», «a», «h1», «h2», «h3», «h4», «span»)
    2) data разбор (чтобы далее можно было работать проще) и сразу callback-и чтобы можно было задавать
    3) приведите примеры с циклами и условиями (вроде всё очевидно, но ощущение, что чего-то не хватает)
    4) документацию, а то совсем не очевидно можно ли передавать null как первый аргумет, какие ошибки могут быть отброшены в каких случаях

    Оценка поверхностная, если что уже есть тогда тестов добавьте)


    1. lxkuz Автор
      03.05.2015 22:12

      Спасибо Вам за отзыв!
      1) согласен, я расширил список тегов. Кроме этого, можно использовать функцию tag для создания любого тега. Интерфейс тот же, только первым параметром идет название тега:

      htmlmake ->
        @tag "car", "bmw x6"
      

      Результат:
      <car>bmw x6</car>
      

      2) Насколько я понял, имеется ввиду такой интерфейс:
      htmlmake ->
        @span data: {hello: "world"}
      

      Ожидаемый результат:
      <span data-hello='world'></span>
      

      Все верно? Тогда я не понял вторую часть «сразу callback-и чтобы можно было задавать», прошу привести пример.
      3, 4) Согласен. Сейчас о всем функционале пока что только по тестам и можно судить.


      1. Louter
        05.05.2015 23:02

        1) не помешало бы возможность конфигурировать (т.е. первый аргумент htmlmake не функция, а параметры)
        2) про дату да, а про коллбеки:

        ->
          @span  
            click: (el, evt) -> ...
            blur:  (el, evt) -> ...
        

        Чтобы объекты не только создавались на лету, но и обработчики событий сразу привязывались к элементам.


        1. lxkuz Автор
          05.05.2015 23:37

          Понятно. На мой взгляд в данном случае профита не много, объясню почему:
          когда мы будем писать обработчики событий, в них нам все равно потребуется работать с дом деревом. В итоге это рискует вылиться и правда в какой-то недо-React) Ведь мое дерево не является виртуальным и не будет автоматически перестраиваться при изменении переменных. Это значит, что прийдется или селектить необходимые элементы и что-то в них менять (за что боролись на то и напоролись), либо вызывать полную перерисовку дерева (это некрасиво).