habrahabr

Создание 3-х уровневого меню с помощью фреймворка Htmlix

  • вторник, 25 июня 2019 г. в 00:18:24
https://habr.com/ru/post/457370/
  • JavaScript
  • Программирование


htmlix — микро фреймфорк на основе data- свойств. Из преймуществ это маленький размер, возможность четко структурировать код, наличие пользовательских событий для обновления DOM а также серверный рендеринг на всех языках по умолчанию, т. к. встраивается в существующюю структуру Html файла.

Сам фреймворк имеет объектно-ориентированную структуру, где каждое свойство — это объект. Получение, запись и удаление свойств происходит с помощью методов setProp(), getProp() и removeProp(), при этом в зависимости от типа свойства приложение само оприделит каким образом оно изменит данное свойство в html, если это класс то будет вызвана функция this.htmlLink.classList.add(«class»).

→ Готовый пример можно посмотреть по ссылке
→ Код примера можно скачать здесь (файлы top-menu-group.html, /js/top-menu-group.js)

Для подключения файлов в проект нужно скопировать папку htmlix по указанной выше ссылке.

Наше меню будет содержать различное количество подпунктов в каждом пункте, и два итоговых варианта ссылки: один это просто текст, второй вариант картинка и текст.

Для создания повторяющихся в html в разных местах и в разном количестве компонентов в htmlix используются виртуальные массивы и свойства с типом данных «group».

Виртуальный массив это массив не содержащий ссылки на Dom элемент с контейнерами которые могут содержать другие компоненты имеющие свойство с типом «render-type» или «group» — как в нашем случае.

Свойства group групируют в себе контейнеры из различных виртуальных массивов.

У нас будет 3-х уровневое меню, соответственно необходимо создать три массива — компонента,
один у нас будет обычный с названием «menu» и два виртуальных с названиями «menu_level_2»,
«menu_level_3».

В javascript это будет выглядеть так:

var State = {
	
	menu: {//первый обычный массив ссылка в html - data-menu="array"
		
		container: "item_level_1", // массив будет содержать контейнеры в html - data-item_level_1="container"

		props: [  /*здесь будет список всех свойств*/],
		methods:{ 
                           /*здесь будет список всех методов обработчиков событий*/
		}		
	},
	virtualArrayComponents: {//обьявляем обьект содержащий виртуальные массивы (не содержат ссылку на DOM)
		
		menu_level_2:{// виртуальный массив, в html коде не присутствует

			container: "item_level_2",//контейнер виртуального массива в html - data-item_level_2="container"

			props: [/*здесь будет список всех свойств*/],
			methods: {

			}
		
	    },
		menu_level_3:{//виртуальный массив
			container: "item_level_3", //контейнер виртуального массива в html - data-item_level_3="container"

			props: [ ],
			methods: {

			}
		
	    },	
	
}}


Далее посмотрим как по отдельности будут выглядеть наши компоненты в html коде:

Массив menu:

     <ul data-menu="array" class="nav-menu-3 pc-width">
						   
	 <li  data-item_level_1="container" data-item_level_1-over='mouseover' data-item_level_1-out='mouseout' >
		<a  href="#" >Пункт меню</a>

                <ul class="hover-non" data-item_level_1-submenuclass="class" data-item_level_1-group="group" >
                  <!-- Здесь будут содержаться контейнеры data-item_level_2="container" из виртуального массива menu_level_2 -  --->

                </u>
         </li>
     </ul>

Здесь:

data-menu=«array» — ссылка на массив первого уровня;
data-item_level_1=«container» — ссылка на контэйнер массива;
data-item_level_1-over='mouseover' — будушее свойство over — обработчик события 'mouseover';
data-item_level_1-out='mouseout' — свойство out обработчик события 'mouseout';
data-item_level_1-submenuclass=«class» — свойство submenuclass — доступ к классам;
data-item_level_1-group=«group» — свойство для групировки контейнеров из виртуальных массивов — массив group[«group-children»] — содержит все обьявленные в html внутри тега — контейнеры data-item_level_2=«container» (все контейнеры item_level_2 — содержатся в виртуальном массиве menu_level_2 а в свойстве group только те которые обьявлены в этом конкретном свойстве контейнера data-item_level_1);

Виртуальный массив menu_level_2

<!-- контейнер виртуального массива -->

<li data-item_level_2="container"  data-item_level_2-over='mouseover' data-item_level_2-out='mouseout'>
	<a  href="#">строка меню</a>
											
        <ul class="hover-non"  data-item_level_2-submenuclass="class" data-item_level_2-group_2="group">
                      <!-- Здесь будут содержаться контейнеры data-item_level_3="container" из виртуального массива menu_level_3 -  --->      
        </ul>
</li>

Здесь:

data-item_level_2=«container» — ссылка на контэйнер виртуального массива menu_level_2;
data-item_level_2-over='mouseover' — будушее свойство over — обработчик события 'mouseover';
data-item_level_2-out='mouseout' — свойство out обработчик события 'mouseout';
data-item_level_2-submenuclass=«class» — свойство submenuclass — доступ к классам;
data-item_level_1-group_2=«group» — свойство для групировки контейнеров из следующих виртуальных массивов menu_level_3 и menu_level_3_img(который мы еще не создали)

Виртуальные массивы menu_level_3 и menu_level_3_img по тому же принципу:


		<li data-item_level_3="container">
				<a  href="#">item-level-3</a>
		 </li>

		<li data-item_level_3_img="container">
			<a  href="#"><img src="/img/Thumbnail_300x300.png">item-level-3</img></a>
		</li>

Они не содержат свойств, только ссылку на html контейнер, но в будущем их можно будет добавить, если возникнет необходимость.

Теперь отредактируем javascript код добавив все свойства и одноименные обработчики к свойствам-событиям

Полностью готовый код будет выглядеть так:

var State = {
	
	menu: {
		
		container: "item_level_1",
		props: [  "submenuclass", "group", "over", "out"],//добавили список свойств

		methods:{ //добавили два обработчика к свойствам  "over", "out"
			over: function(){

						//this - в обработчике указывает на конкретный dom элемент (this.htmlLink) и его свойство 
                                                  //получаем доступ к родительскому контейнеру и смежному свойству submenuclass  с нашим свойством (находящемся в общем контейнере), после чего удаляем класс  "hover-non" чтобы отобразить подпункт меню.

				this.parentContainer.props.submenuclass.removeProp("hover-non");
			},
			out: function(){
						
				this.parentContainer.props.submenuclass.setProp("hover-non");
			},
		}		
	},
	virtualArrayComponents: {//виртуальные массивы 
		
		menu_level_2:{
			container: "item_level_2",
			props: [ "group_2", "submenuclass",  "over", "out"],
			methods: {
					click: function(){ //аналогичная функция.
						
						this.parentContainer["group-parent"].removeFromGroup(this.parentContainer["group-id"]);//.clearGroup();//
						
						console.log(this.rootLink.state);
						
					},
					over: function(){
						
						this.parentContainer.props.submenuclass.removeProp("hover-non");
					},
					out: function(){
						
						this.parentContainer.props.submenuclass.setProp("hover-non");
					},
			}
		
	    },
		menu_level_3:{
			container: "item_level_3",
			props: [ ],
			methods: {

			}
		
	    },	

		menu_level_3_img:{
			container: "item_level_3_img",
			props: [ ],
			methods: {

			}
		
	    },		
	},	
	eventEmiters: {
		

	}	
}
window.onload = function(){
///создаем экземпляр  HTMLix
var HM = new HTMLixState(State);

console.log(HM);
}


Вот в принципе и весь javascript-код.

Html-код с уже помещенными в друг друга компонентами будет выглядеть так:

<nav >
	<ul data-menu="array" class="nav-menu-3 pc-width">
		<!--контейнер из  массива menu -->					   
		<li  data-item_level_1="container" data-item_level_1-over='mouseover' data-item_level_1-out='mouseout' >
			<a  href="#" >Пункт меню</a>									
			<ul class="hover-non" data-item_level_1-submenuclass="class" data-item_level_1-group="group" ><!--class="sub-nav-menu"-->
									
			<!--контейнер из виртуального массива menu_level_2 -->

                              <li data-item_level_2="container"  data-item_level_2-over='mouseover' data-item_level_2-out='mouseout'>
			          <a  href="#">строка меню</a>											
			          <ul class="hover-non"  data-item_level_2-submenuclass="class" data-item_level_2-group_2="group">

				 <!--контейнер из виртуального массива menu_level_3_img -->				
					  <li data-item_level_3_img="container">
					       <a  href="#"><img src="/img/Thumbnail_300x300.png">item-level-3</img></a>
					   </li>
							
                                             <li data-item_level_3_img="container">
						     <a  href="#"><img src="/img/Thumbnail_300x300.png">item-level-3</img></a>
					      </li>										
                                       	       <li data-item_level_3_img="container">
					              <a  href="#"><img src="/img/Thumbnail_300x300.png">item-level-3</img></a>
							</li>													 												 
					</ul>
				</li>

				<li data-item_level_2="container" data-item_level_2-over='mouseover' data-item_level_2-out='mouseout'>
					<a  href="#">строка меню</a>
											
					<ul class="hover-non"  data-item_level_2-submenuclass="class" data-item_level_2-group_2="group">
											
	
					          <li data-item_level_3_img="container">
							 <a  href="#"><img src="/img/Thumbnail_300x300.png">item-level-3</img></a>
						  </li>
											    
                                                  <li data-item_level_3_img="container">
						           <a  href="#"><img src="/img/Thumbnail_300x300.png">item-level-3</img></a>
						  </li>												 
					</ul>											
				</li>

<!-- далее по тому же принципу в зависимости от нужного нам количества пунктов и подпунктов меню -->

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

Краткая документация по всем основным свойствам а также туториалы к некоторым примерам приложений можно почитать здесь.