Данное приложение — это страница простого фильтра товаров по категориям, с различными частями шаблона в карточке товара.
Полный пример данного приложения а также других, можно посмотреть по ссылке на гитхабе:
Ссылка на примеры, данный пример: page-template.html, /js/page-template.js
Wiki с перечнем всех стандартных свойств и методов приложения можно посмотреть
здесь
Рабочий пример приложения можно покликать здесь
Более легкий для понимания код можно посмотреть по ссылке:
Пошаговое руководство по созданию элементов в Htmlix
Htmlix — это микро фреймворк для построения фронтенда на javascript. Принцип его работы базируется на data- атрибутах.
Суть его работы заключается в том что мы с помощью data- свойств создаем структуру приложения в html файле добавляя свойства, обработчики событий и слушателей.
А затем перечисляем их названия в js коде(чтобы получить к ним доступ), создание всех обьектов и прикрепления событий происходит автоматически путем определения типов данных прикрепленных в data свойствах.
Проще просто посмотреть код готового примера чтобы понять как все работает, в этой статье дается краткое описание хода создания структуры приложения.
Для начала работы в html файле делается предварительное описание структуры приложения путем прикрепления data атрибутов с указанием их типа (тип атрибута в кавычках),
например:
<div class=" row" data-cards="array" data-cards-listenfetch="emiter-fetch-posts" data-cards- listenrout="emiter-router" data-cards-displayall="style">
<div data-card="container" class="col-4 card-in">
<h5 data-card-title="text">Название 1</h5>
<a data-card-click="click" data-card-href="href" href="/page/card?id=0">
<img data-card-srcimg="src" src="/img/images.jpg" />
</a>
<p data-card-paragraf="text">Краткое описание 1</p>
</div>
<div data-card="container" class="col-4 card-in">
<h5 data-card-title="text">Название 2</h5>
<a data-card-click="click" data-card-href="href" href="/page/card?id=1">
<img data-card-srcimg="src" src="/img/Thumbnail_300x300.png" />
</a>
<p data-card-paragraf="text">Краткое описание 2</p>
</div>
.......................
Здесь:
data-cards=«array» — создаем массив однотипных элементов
data-card=«container» — контейнеры это набор однотипных элементов, либо это один элемент
если он не помещен в массив, контейнеры содержат все изменяемые и получаемые свойства, а также обработчики и слушатели событий.
data-card-title=«text» — свойство которое содержит доступ к тексту из javscript (this.title.getProp(), .setProp())
data-card-click=«click» — прикрепляет обработчик события click к данному тегу
data-cards-listenfetch=«emiter-fetch-posts» — прикрепляет обработчик пользовательского события «emiter-fetch-posts» к свойству контейнера.
Пользовательские события оповещают все свойства к которым они прикреплены при помощи вызова функции из любого участка кода с названием события. Например this.eventProps[«emiter-fetch-posts»].setProp(«posts») — вызовет на всех слушателях, событие «emiter-fetch-posts», в котором можно будет получить измененные данные «posts».
Для свойств содержащих стандартные события имеется также два дополнительных метода disableEvent() и enableEvent() выключение и включение события на конкретном элементе (чтобы полностью его не удалять).
Все свойства содержатся в контейнерах (data-card=«container»), если это однотипные контейнеры и их необходимо будет добавлять, либо удалять в процессе работы приложения, групируются в массивы (data-cards=«array»).
Именование свойств происходит следующим образом: после data идет название контейнера в котором хранится данное свойство, далее идет название свойства либо функции, затем после знака = в кавычках тип данного свойства например «class», если это событие то пишется имя события например «click», если это пользовательское событие то «emiter-далее название события», например «emiter-fetch-posts». При создании объекта свойств тип важен для того чтобы определить, что необходимо делать с данным свойством. Если это событие то будет поиск одноименной функции и добавление соответствующего обработчика.
Контейнер находящийся в массиве можно удалить вызвав метод .remove() либо с корневого приложения по id вызвав this.rootLink.removeByIndex(nameArray,id), id — контейнера это его порядковый номер в массиве и может измениться при добавлении или удалении соседних контейнеров.
Далее, после добавления всех свойств и определения структуры приложения в html, идет описание приложения в javascript, путем перечисления всех имен массивов, контейнеров, свойств, и методов обработчиков событий, например:
var State = {
cards:{ /* название массива (data-cards="array") */
container: 'card', /* название контейнера (data-card="container"), обратите внимание контейнер находящийся в массиве называется по другому */
arrayProps: ["listenrout", "listenfetch", "displayall"], /* перечесляем список свойств и методов обьявленных в html для массива (data-cards-displayall="style")*/
arrayMethods: { /* методы массива */
listenrout: function(){
/* слушаем событие ["emiter-router"] (в html - listenrout="emiter-router") */
if(this.emiter.prop == "/page-template.html"){
/* получаем данные события */
this.parentContainer.props.displayall.setProp("")
/* доступ к другим свойствам находящимся в том же массиве, что и данное свойство */
}else{
this.parentContainer.props.displayall.setProp("display: none;")
}
},
listenfetch: function(){
/* слушаем событие ["emiter-fetch-posts"] (в html - listenrout="emiter-fetch-postsr") */
var newArray = this.emiter.prop;
this.rootLink.clearContainer(this.pathToContainer);
for(var i =0; i< newArray.length; i++){
this.rootLink.createContainerInArr(this.pathToContainer, {
/* создаем новые контейнеры для данных пришедших с сервера предварительно удалив старые */
title: newArray[i].title,
paragraf: newArray[i].paragraf_short,
href: newArray[i].href,
srcimg: newArray[i].srcimg
});
}
this.rootLink.stateProperties.cards = newArray;
/* перезаписываем массив с карточками товара */
}
},
props: ['title','paragraf',"click", 'srcimg', "href"],
/* перечисляем список свойств и методов объявленных в html для контейнера */
methods: {
click: function(event){ <!-- обработчик события click (data-card-click="click")-->
event.preventDefault();
var href = this.parentContainer.props.href.getProp();
/* получаем свойство (data-card-href="href") находящееся в том же контейнере что и обработчик события */
var cardId = href.split("?")[1].split("=")[1];
window.history.pushState(
null,
href,
href
);
/* вызываем два пользовательских события и передаем им новые данные */
this.rootLink.eventProps["emiter-router"].setEventProp(href);
this.rootLink.eventProps["emiter-single-id"].setEventProp(cardId);
}
}
},
.......................
Пользовательские события необходимо также регистрировать в описании объекта, пример:
eventEmiters: {
["emiter-router"]: { /* регистрация событий с начальными данными */
prop: "/page-template.html"
},
["emiter-single-id"]: {
prop: "0"
},
["emiter-fetch-posts"]: {
prop: "",
},
Затем при вызове к примеру: this.rootLink.eventProps[«emiter-router»].setEventProp(href);
все слушатели события [«emiter-router»] (listenrout=«emiter-router» — в html коде) смогут получить новые данные в своих обработчиках и обновить себя на их основании, как в коде ниже:
listenrout: function(){
/* слушаем событие ["emiter-router"] (в html-listenrout="emiter-router") */
if(this.emiter.prop == "/page-template.html"){/* получаем данные события */
this.parentContainer.props.displayall.setProp("");
/* доступ к другим свойствам находящимся в том же массиве, что и данное свойство */
}else{
this.parentContainer.props.displayall.setProp("display: none;")
}
},
Также в описание можно добавить статические переменные (переменные которые не вызывают обработчиков событий при их изменении):
stateProperties: {
cards: "",
},
Общие для всего приложения методы:
stateMethods: {
fetchPosts: function(nameFile, callb){
fetch('/json/'+nameFile+'.json')
.then((response) => {
if(response.ok) {
return response.json();
}
throw new Error('Network response was not ok');
})
.then((json) => {
callb(json);
})
.catch((error) => {
console.log(error);
});
},
},
Компоненты которые будут загружены асинхронно с html шаблона компонентов (template.html)
fetchComponents: {
variants1: {
/* данный компонент будет загружен асинхронно, с директории по умолчанию /templete/template.html */
container: "variant1",
props: ["clickvariant", "text"],
methods: {
clickvariant: function(event){
event.preventDefault();
this.rootLink.eventProps["emiter-chose-variant"].setEventProp(this.parentContainer.props.text.getProp());
//console.log(this);
},
}
},
Создается экземпляр приложения с помощью функции:
window.onload = function (){
var HM = new HTMLixState(State); /* создаем экземпляр приложения */
HM.stateMethods.fetchPosts('category1', function(jsonData){ HM.stateProperties.cards = jsonData });
console.log(HM);
}
В консоли можно посмотреть структуру созданного объекта где:
- description- обьект свойство содержит описание приложения (всех объектов), список всех свойств, контейнеров, методов, 'событийных' переменных и т.д.
- eventProps — список всех обьявленных 'событийных' переменных (emiter) и их свойств (prop) вызывающих события для всех подписчиков(используются для обновления DOM)
- state — содержит все контейнеры и массивы с готовыми обьектами, методами, ссылками и т. д.
- stateMethods- методы для общего пользования (не конкретным свойством)
- stateProperties- статические переменные(не обновляют DOM при изменении).
Это был краткий обзор создания приложения, на htmlix.
Сам фреймворк имеет объектно-ориентированную структуру, где каждое свойство — это объект. Получение, запись и удаление свойств происходит с помощью методов setProp(), getProp() и removeProp(), при этом в зависимости от типа свойства приложение само оприделит каким образом оно изменит данное свойство в html, если это класс то будет вызвана функция this.htmlLink.classList.add(«class»).
Методов в фремворке пока не очень много, с ними можно познакомиться посмотрев исходный код где есть краткое описание к основным используемым в ходе работы. Поэтому тем кто неплохо знает javscript разобраться не должно составить большого труда. Пока что htmlix находится в тестовой версии, однако уже сейчас с помощью него можно решать многие типовые задачи фронтенд разработки.
Комментарии (3)

sfi0zy
17.06.2019 19:38+3Желание изучить, как такие штуки работают изнутри, и сделать что-то свое — это хорошо. Даже если не взлетит (а скорее всего так и будет), то опыта даст точно. Но. Раз уж приносите свой инструмент людям, то было бы неплохо:
1. Отформатировать код. Даже автоматическое форматирование пойдет. Сейчас оценить примеры и конструктивно покритиковать сложно — глаза вытекают.
2. Сделать отдельный репозиторий с самим фреймворком или как-то переструктурировать этот, чтобы было понятно, что вот тут инструмент, а вот тут примеры. Вот тут один пример, а вот тут — другой. Ну и NPM + CDN для быстрого старта будет очень кстати.
3. Набросать минимальную документацию. А еще лучше к ней добавить пару демок на CodePen, где можно по-быстрому потыкать. Как говорится, лучше один раз потыкать, чем десять раз почитать. Все это на GitHub Pages можно захостить — бесплатно и удобно.
Я тоже делал инструмент, чтобы структурировать лапшу в поделках «для себя», и думаю вам стоит на него посмотреть.

Mihail127 Автор
18.06.2019 02:10Извеняюсь за не очень хорошую читабельность, постараюсь исправить формат подачи, и добавить более ясное и читаемое описание.
malinichev
Форматирование оставляет желать лучшего, поэтому статья не читабельна