Go away HTML, go away

Тенденция последних лет в веб-разработке — фреймворки и компиляторы (сборщики, если кому то угодно). Они везде: в javascript, css, подключаемых библиотеках и т.д. Javascript фреймворки повсеместно используют шаблоны для отрисовки всего и вся. Но у всех них есть одна общая черта — HTML. Громоздкий, свёрстанный, местами от проекта к проекту контрл+ц — контрл+в… Весь этот html всегда лежит кусками, прикрепляется конструкциями вида script type=«text/template». В общем, как будто здесь еще не придумали, как с ним бороться.

В один момент я подумал, а почему бы не перевести всю вёрстку в JSON. Он компактен, красноречив и очень хорошо читабелен (я имею ввиду сервисы а-ля jsoneditoronline.org). Порыскав готовые библиотеки понял, что эту верстку в json только усложняют и никто не сделал очевидного и простого.

И сделал — randr. Библиотечка небольшая, работает шустро.

Работает так: на вход одноимённой функции даёте определенный json, на выходе — готовый DOM (или какая-то его часть):

var json = {
 node: 'div',
 content: 'Hello World'
}

document.body.appendChild(randr(json));

randr из этого json`а сделает div с текстом «Hello World» внутри.

Еще можно передавать атрибуты:

var json = {
 node: 'div',
 defaults: [
  { type: 'class', data: 'super-class' },
  { type: 'attr', data: 'some attr value' }
 ],
 content: 'Hello World'
}

document.body.appendChild(randr(json));

Отлично! у нашего div`а появились атрибуты class и attr. Однажды мне finom подсказал сделать «defaults» в виде объекта со свойствами, так говорит компактнее. Сказано — сделано:

var json = {
 node: 'div',
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 content: 'Hello World'
}

document.body.appendChild(randr(json));

Да, стало компактнее. Оставил оба способа. Но уменьшать объем можно и другим способом. Обычно текст (в роли контента) заключен в теги «p». Именно, так мы и поступим, уберём ноду «div»:

var json = {
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 content: 'Hello World'
}

document.body.appendChild(randr(json));

Получилось! По умолчанию если отсутствует «node» а «content» является текстом — рисуем «p», иначе будет «div». Да, можно просто не писать эти пресловутые div:

var json = {
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 content: {
   content: {
     content: 'Хватит уже изголяться'
   }
 }
}

document.body.appendChild(randr(json));

Надеюсь ясно, что тут будет параграф, обёрнутый в 2 div`а. И да, контент может быть разный. Например массив:

var json = {
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 content: [
  { content: 'параграф 1 с текстом' },
  { content: 'параграф 2 с текстом' }
 ]

document.body.appendChild(randr(json));

Вот уже лучше, но можно добавить еще кое-что! Наследование атрибутов:

var json = {
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 implement: true,
 content: [
  { content: 'параграф 1 с текстом' },
  { content: 'параграф 2 с текстом' }
 ]

document.body.appendChild(randr(json));

Видно, что появился флаг implement: true — именно он отвечает за передачу атрибутов дочерним элементам. Но дети бывают капризные и не захотят принимать это наследство:

var json = {
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 implement: true,
 content: [
  { content: 'параграф 1 с текстом', extend: false },
  { content: 'параграф 2 с текстом' }
 ]

document.body.appendChild(randr(json));

Думаю, тут тоже ясно. Отрицательный флаг extend не позволяет наследовать атрибуты.
Наследуются атрибуты по принципу затирания родительских дочерними, кроме стилевых классов. У них происходит склеивание — «родительский-класс дочерний-класс».

Зачем вообще наследовать их кончено каждый решит сам. Я лично использую для вспомогательных классов вроде «float-right, center, background-red». Думаю, придумать можно. Но никто не заставляет пользоваться.

Есть еще фишечка — создание своих собственных «нод». Так сказать готовых блоков хитро сверстанных элементов или просто повторяющихся заготовок:

var myNodes = {
 askDelete: function(content){
  var container = document.createElement('div');
  var bYes = document.createElement('button');
  var bNo = document.createElement('button');
  bYes.innerHTML = content.yes;
  bNo.innerHTML = content.no;
  container.appendChild(bYes);
  container.appendChild(bNo);
  return container;
 }
}

var json = {
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 content: [
  { content: 'параграф 1 с текстом' },
  {
    node: 'askDelete',
    content: {
      yes: 'Да, удалить',
      no: 'Нет конечно'
    }
  }
 ]

document.body.appendChild(randr(json, myNodes));

В этом коде мы создали нашу волшебную ноду «askDelete» в объекте «myNodes» и передали объект вторым параметром в randr. А в json мы вызываем эту волшебную ноду и в качестве параметров используем свойство «content». Таким образом можно создавать свои заготовочки и внедрять их в разнообразные проекты.

Если же вам нужно получить от randr не DOM, а верстку в виде текста, то просто добавьте третий параметр «true» в randr:

var myNodes = {
 askDelete: function(content){
  var container = document.createElement('div');
  var bYes = document.createElement('button');
  var bNo = document.createElement('button');
  bYes.innerHTML = content.yes;
  bNo.innerHTML = content.no;
  container.appendChild(bYes);
  container.appendChild(bNo);
  return container;
 }
}

var json = {
 defaults: {
  class: 'super-class',
  attr: 'some attr value'
 },
 content: [
  { content: 'параграф 1 с текстом' },
  {
    node: 'askDelete',
    content: {
      yes: 'Да, удалить',
      no: 'Нет конечно'
    }
  }
 ]

document.body.innerHTML = randr(json, myNodes, true);

На этом всё. Думаю, такая реализация вёрстки с помощью json будет вам полезна. По крайней мере занимает меньше места и можно передавать в Ajax в виде объектов.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 19

    +12
    React JS
      +3
        +4
        Идея концептуально верная, и в общем-то похожа на веб-компоненты, но реализация слишком механическая и лучше подходит как формат для визуального редактора кода, чем для написания руками. Сравним:

        <img src="./smile.png"/>
        

        и

        {
            "node":"img",
            "defaults":[
                {"type":"src", "data":"./smile.png"}
            ]
        }
        

        Это не наглядно и громоздко. Почему не использовать такой вариант:

        {
            "IMG":{"src":"./smile.png"}
        }
        


        Более сложный пример:
        {
            "DIV": {"class":"plate"},
            "content":[
                {"H3":"Good news everyone"},
                {"P":"It works!"}
            ]
        }
        

        Да, многие недолюбливают капс, но в данном случае это не большая плата за наглядность и компактность.
          0
          Да, ваш вариант короче и возможно доделать с таким синтаксисом, но в основном опирался на наглядность самого дерева в виде объекта
            0
            Тогда уж, наверное, как-то
            { "DIV": { "class": "plate", "content": [ {"H3": "Good news everyone"}, {"P": "It works!"} ] } }

            upd: извините за не слишком читаемый вид — от благодати тэгов я, похоже, отлучён.
              0
              Нет, именно так, как я указал – у мета-тегов атрибут «content» – это именно атрибут.
                0
                И что это меняет? Обработчик вполне может распознать тип и по нему создать либо атрибут, либо набор вложенных тэгов. А структура в таком виде выходит проще и элегантнее.
              0
              У себя сделали так
              load(["div", { "class": "plate" }, [
                  ["h3", { innerHTML: "Good news everyone" }],
                  ["p", { innerHTML: "It works" }]
              ]]);
              

              тип элемента однозначно позволяет определить что это: имя, свойства или дети элемента.
                0
                По мне такое было бы приятный:
                {
                ".plate": {
                " h3": «foo»,
                " p#text": «bar»
                }
                }
                Да и не понятно зачем вообще нужны отдельно div, class и пр. Массив в content имхо тоже избыточен.
                P's: сорри за сумбур — пишу с мобилы.
                  +3
                  А в чем принципиальная разница между
                  <img src="./smile.png"/>
                  

                  и
                  "IMG":{"src":"./smile.png"}
                  


                  И вообще (это уже комментарий к самой статье), я так и не понял зачем менять HTML на JSON
                  +2
                  Я бы написал свой транслятор (в json), убрав кавычки,: и { }. А за основу сделал бы блоки как в coffeescript (пробелами и новыми строками). Да и по умолчанию лучше создавать textnode а не p.
                    +6
                    ru.wikipedia.org/wiki/Haml
                    0
                    Ещё на эту тему пример на D3.js
                    Код
                    var cols = ['name', 'sex', 'age'];
                    var data = [
                        {name: 'Ivan', sex: 'Male', age: 27},
                        {name: 'Denis', sex: 'Male', age: 20},
                        {name: 'Elena', sex: 'Female', age: 23},
                    ];
                    var table = d3.select('body').append('table');
                    
                    table
                        .append('thead')
                        .append('tr').selectAll('th').data(cols)
                        .enter().append('th')
                        .style('padding','5px')
                        .text(function (d) {
                            return d.toUpperCase();
                        });
                    
                    table
                        .append('tbody').selectAll('tr').data(data)
                        .enter().append('tr').selectAll('td').data(function (row) {
                            return d3.permute(row, cols);
                        })
                        .enter().append('td')
                        .style('padding','5px')
                        .text(function (d) { return d; });
                    

                    http://jsfiddle.net/fszhuLk5/
                      0
                      Сделали свой проект это классно. Вы потом поискали, что уже сделали другие?
                      Лучший из известных мне результатов это Haml, ссылку на который выше уже привели. Краток, гибок. Этих двух характеристик хватает для того, чтобы он был почти идеален.
                        –1
                        Haml просто так вы не пришлёте с сервера, просто сгенерив скажем на php. Ну и на стороне клиента развернуть — ни css подключить. Json один из самых ходовых форматов, именно он и был взят за основу, так как веб всё ещё состоит из css+html+js. Одной составляющей яваскрипта является json… И да, я предварительно поглядел штук 12-15 библиотек, которые из json собирают страницу. Все они громоздкие и с сложным синтаксисом. Мой оказался проще.
                          0
                          Громоздкий, свёрстанный, местами от проекта к проекту контрл+ц — контрл+в… Весь этот html всегда лежит кусками, прикрепляется конструкциями вида script type=«text/template». В общем, как будто здесь еще не придумали, как с ним бороться.
                          Я ответил на вот эту вашу претензию к HTML. Хотя она скорее к криворуким верстальщикам. И то, что бороться с этим уже придумали как.

                          И HAML можно отправлять так же, как и JSON, и рендерить на стороне клиента, как вы делаете это с JSON.
                          А вот указанная вами проблема с громоздкостью в вашем подходе не снята, закрывающие теги заменены на закрывающие скобки, что ещё хуже, потому что по закр. тегу видно от чего он, а по скобкам нет. Хотя это тоже всё давно решено подсветкой в среде программирования. Но HAML принципиально убирает проблему громоздкости, чего не делает ваш подход.

                          И да, я предварительно поглядел штук 12-15 библиотек, которые из json собирают страницу. Все они громоздкие и с сложным синтаксисом. Мой оказался проще.
                          Я имел ввиду решения с громоздкостью HTML, а не генераторы для вашего подхода. Т.е. вы сделали своё решение, а потом посмотрели, кто уже его реализует. А нужно было посмотреть кто реализует решение описанной в первом абзаце проблемы.
                        –1
                        1. Почему не указано, что для работы требуется jquery?
                        2. Бегло глянув сорс заметил что jquery используется лишь для setAttribute & getAttribute & appendChild и добавление эвента. Вы блин серьезно для этого таскаете всю либу, что делается 2..3 строчками на pure js?
                        3. Описание html в json в результате очень громозкое, как мне кажется
                        4. Практическое применение — это легкая альтернатива шаблонизаторам или замена тех зачастую железобетонных html паттернов, что инклудятся в документ из js в обход createNode для повышения быстродействия в целом? На первое совсем не похоже, а второе — нет, ну правда, покажите пример с практической потребностью?
                        5. Это опыт, вы наверняка не плохо потренировали свои навыки во время работы над этим проектом, молодцом!
                          –1
                          Нет, jquery там совсем не нужен. Беглый взгляд вас подвёл. А насчёт ивентов, так я их и не описывал. Это тестовая наработка, которая никого и не касается. В самом начале я так и написал что фреймверки грузят страшные шаблоны пачками, а можно их облагородить, вот и всё. Сразу понасливали карму…
                            0
                            А это видимо так, просто значок доллара :)
                            Строка 49 «randr.js»: $(node).on(k.events[i].type,k.events[i].action);

                            Ваша тестовая наработка находится в паблике, что является… не совсем правильно. Есть такая штука как "ответвления", и она довольно удобна, должен признать. Не говоря уже про то, что она принята стандартом де-факто.

                            И на счет вашего крайнего предложения в комментарии — никогда не говорите о том, что вы упомянули в крайнем предложении своего комментария (с) хабранародная мудрость

                        Only users with full accounts can post comments. Log in, please.