ÐомпоненÑÑ, Ñ Ð¾ÑÑ Ð¸ каждÑй Ñам по Ñебе, обÑÑно как-Ñо обÑаÑÑÑÑ Ñ Ð¾ÑÑалÑной ÑаÑÑÑÑ ÑÑÑаниÑÑ
ÐÑÑÑ Ð½ÐµÑколÑко ÑпоÑобов, пÑи помоÑи коÑоÑÑÑ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½ÑÑ ÑообÑаÑÑ Ð´ÑÑг дÑÑÐ³Ñ Ð¾ важнÑÑ ÑобÑÑиÑÑ , коÑоÑÑе в Ð½Ð¸Ñ Ð¿ÑоизоÑли.
ÐолбÑки
ÐолбÑк (Ð¾Ñ Ð°Ð½Ð³Ð». callback) â ÑÑо ÑÑнкÑиÑ, коÑоÑÑÑ Ð¼Ñ Ð¿ÐµÑедаÑм кÑда-либо и ожидаем, ÑÑо она бÑÐ´ÐµÑ Ð²Ñзвана пÑи наÑÑÑплении ÑобÑÑиÑ.
ÐапÑимеÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ добавиÑÑ Ð² options
Ð´Ð»Ñ Menu
новÑй паÑамеÑÑ â ÑÑнкÑÐ¸Ñ onselect
, коÑоÑÐ°Ñ Ð±ÑÐ´ÐµÑ Ð²ÑзÑваÑÑÑÑ Ð¿Ñи вÑбоÑе пÑнкÑа менÑ:
var menu = new Menu({
title: "СладоÑÑи",
template: _.template(document.getElementById('menu-template').innerHTML),
listTemplate: _.template(document.getElementById('menu-list-template').innerHTML,
items: {
"donut": "ÐонÑик",
"cake": "ÐиÑожное",
"chocolate": "Шоколадка"
},
onselect: showSelected
});
function showSelected(href) {
alert(href);
}
Ркоде Ð¼ÐµÐ½Ñ Ð½Ñжно бÑÐ´ÐµÑ Ð²ÑзÑваÑÑ ÐµÑ, напÑÐ¸Ð¼ÐµÑ Ñак:
...
function select(link) {
options.onselect(link.getAttribute('href').slice(1));
...
}
...
ÐолнÑй пÑимеÑ:
function Menu(options) {
var elem;
function getElem() {
if (!elem) render();
return elem;
}
function render() {
var html = options.template({
title: options.title
});
elem = document.createElement('div');
elem.innerHTML = html;
elem = elem.firstElementChild;
elem.onmousedown = function() {
return false;
}
elem.onclick = function(event) {
if (event.target.closest('.title')) {
toggle();
}
if (event.target.closest('a')) {
event.preventDefault();
select(event.target.closest('a'));
}
}
}
function renderItems() {
if (elem.querySelector('ul')) return;
var listHtml = options.listTemplate({
items: options.items
});
elem.insertAdjacentHTML("beforeEnd", listHtml);
}
function select(link) {
options.onselect(link.getAttribute('href').slice(1));
}
function open() {
renderItems();
elem.classList.add('open');
};
function close() {
elem.classList.remove('open');
};
function toggle() {
if (elem.classList.contains('open')) close();
else open();
};
this.getElem = getElem;
this.toggle = toggle;
this.close = close;
this.open = open;
}
.menu ul {
display: none;
margin: 0;
}
.menu .title {
font-weight: bold;
cursor: pointer;
}
.menu .title:before {
content: 'â¶';
padding-right: 6px;
color: green;
}
.menu.open ul {
display: block;
}
.menu.open .title:before {
content: 'â¼';
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="menu.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
<script src="https://cdn.polyfill.io/v1/polyfill.js?features=Element.prototype.closest"></script>
<script src="menu.js"></script>
</head>
<body>
<script type="text/template" id="menu-template">
<div class="menu">
<span class="title"><%-title%></span>
</div>
</script>
<!--
вÑÑÑÐ¾ÐµÐ½Ð½Ð°Ñ Ð±ÑаÑзеÑÐ½Ð°Ñ ÑÑнкÑÐ¸Ñ encodeURIComponent кодиÑÑÐµÑ ÑпеÑ-ÑÐ¸Ð¼Ð²Ð¾Ð»Ñ Ð´Ð»Ñ URL,
напÑÐ¸Ð¼ÐµÑ ÑÑÑÑкие бÑÐºÐ²Ñ Ð¸ пÑобелÑ
в ÑÑом пÑимеÑе ÑÑÑÑкиÑ
бÑкв в клÑÑаÑ
items неÑ, но поÑенÑиалÑно они возможнÑ
-->
<script type="text/template" id="menu-list-template">
<ul>
<% for(var name in items) { %>
<li>
<a href="#<%=encodeURIComponent(name)%>">
<%-items[name]%>
</a>
</li>
<% } %>
</ul>
</script>
<script>
var menu = new Menu({
title: "СладоÑÑи",
template: _.template(document.getElementById('menu-template').innerHTML.trim()),
listTemplate: _.template(document.getElementById('menu-list-template').innerHTML.trim()),
items: {
cake: "ТоÑÑ", // cake <a href="#cake">ТоÑÑ</a>
donut: "ÐонÑик", // donut
chocolate: "Шоколадка" // chocolate
},
onselect: showSelected
});
function showSelected(itemName) {
alert(itemName);
}
document.body.appendChild(menu.getElem());
</script>
</body>
</html>
Свои ÑобÑÑиÑ
Ðак Ð¼Ñ Ñже знаем, в ÑовÑеменнÑÑ Ð±ÑаÑзеÑÐ°Ñ DOM-ÑлеменÑÑ Ð¼Ð¾Ð³ÑÑ Ð³ÐµÐ½ÐµÑиÑоваÑÑ Ð¿ÑоизволÑнÑе ÑобÑÑÐ¸Ñ Ð¿Ñи помоÑи вÑÑÑоеннÑÑ Ð¼ÐµÑодов, а в IE8- ÑÑо возможно Ñ Ð¸ÑполÑзованием ÑÑеймвоÑка, к пÑимеÑÑ, jQuery.
ÐоÑполÑзÑемÑÑ Ð¸Ð¼Ð¸, ÑÑÐ¾Ð±Ñ ÐºÐ¾Ñневой ÑÐ»ÐµÐ¼ÐµÐ½Ñ Ð¼ÐµÐ½Ñ Ð³ÐµÐ½ÐµÑиÑовал ÑобÑÑие, коÑоÑое Ð¼Ñ Ð½Ð°Ð·Ð¾Ð²Ñм select
, пÑи вÑбоÑе ÑлеменÑа, и пеÑедавал в обÑÐµÐºÑ ÑобÑÑÐ¸Ñ Ð²ÑбÑанное знаÑение.
ÐÐ»Ñ ÑÑого модиÑиÑиÑÑем ÑÑнкÑÐ¸Ñ select
:
function Menu(options) {
...
function select(link) {
var widgetEvent = new CustomEvent("select", {
bubbles: true,
// detail - ÑÑандаÑÑное ÑвойÑÑво CustomEvent Ð´Ð»Ñ Ð¿ÑоизволÑнÑÑ
даннÑÑ
detail: link.getAttribute('href').slice(1)
});
elem.dispatchEvent(widgetEvent);
}
...
}
Ðод, коÑоÑÑй заинÑеÑеÑован в Ñом, ÑÑÐ¾Ð±Ñ ÑзнаваÑÑ, ÑÑо вÑбÑано в менÑ, подпиÑÑваеÑÑÑ Ð½Ð° ÑобÑÑие select
его коÑневого ÑлеменÑа:
var menu = new Menu(...);
var elem = menu.getElem();
elem.addEventListener('select', function(event) {
alert( event.detail );
});
ÐмеÑÑо detail
можно бÑло Ð±Ñ Ð²ÑбÑаÑÑ Ð¸ дÑÑгое название ÑвойÑÑва, но Ñогда нÑжно позабоÑиÑÑÑÑ Ð¾ Ñом, ÑÑÐ¾Ð±Ñ Ð¾Ð½Ð¾ не конÑликÑовало Ñо ÑÑандаÑÑнÑми. ÐÑоме Ñого, в конÑÑÑÑкÑоÑе CustomEvent
ÑазÑеÑено ÑолÑко detail
, дÑÑгое ÑвойÑÑво понадобилоÑÑ Ð±Ñ Ð¿ÑиÑваиваÑÑ Ð² оÑделÑной ÑÑÑоке.
ÐолнÑй пÑимеÑ:
function Menu(options) {
var elem;
function getElem() {
if (!elem) render();
return elem;
}
function render() {
var html = options.template({
title: options.title
});
elem = document.createElement('div');
elem.innerHTML = html;
elem = elem.firstElementChild;
elem.onmousedown = function() {
return false;
}
elem.onclick = function(event) {
if (event.target.closest('.title')) {
toggle();
}
if (event.target.closest('a')) {
event.preventDefault();
select(event.target.closest('a'));
}
}
}
function renderItems() {
if (elem.querySelector('ul')) return;
var listHtml = options.listTemplate({
items: options.items
});
elem.insertAdjacentHTML("beforeEnd", listHtml);
}
function select(link) {
var widgetEvent = new CustomEvent("select", {
bubbles: true,
detail: link.getAttribute('href').slice(1)
});
elem.dispatchEvent(widgetEvent);
}
function open() {
renderItems();
elem.classList.add('open');
};
function close() {
elem.classList.remove('open');
};
function toggle() {
if (elem.classList.contains('open')) close();
else open();
};
this.getElem = getElem;
this.toggle = toggle;
this.close = close;
this.open = open;
}
.menu ul {
display: none;
margin: 0;
}
.menu .title {
font-weight: bold;
cursor: pointer;
}
.menu .title:before {
content: 'â¶';
padding-right: 6px;
color: green;
}
.menu.open ul {
display: block;
}
.menu.open .title:before {
content: 'â¼';
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="menu.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
<script src="https://cdn.polyfill.io/v1/polyfill.js?features=CustomEvent,Element.prototype.closest"></script>
<script src="menu.js"></script>
</head>
<body>
<script type="text/template" id="menu-template">
<div class="menu">
<span class="title"><%-title%></span>
</div>
</script>
<script type="text/template" id="menu-list-template">
<ul>
<% for(var name in items) { %>
<li>
<a href="#<%=encodeURIComponent(name)%>">
<%-items[name]%>
</a>
</li>
<% } %>
</ul>
</script>
<script>
var menu = new Menu({
title: "СладоÑÑи",
template: _.template(document.getElementById('menu-template').innerHTML.trim()),
listTemplate: _.template(document.getElementById('menu-list-template').innerHTML.trim()),
items: {
cake: "ТоÑÑ", // cake <a href="#cake">ТоÑÑ</a>
donut: "ÐонÑик", // donut
chocolate: "Шоколадка" // chocolate
}
});
var elem = menu.getElem();
document.body.appendChild(elem);
elem.addEventListener('select', function(event) {
alert(event.detail);
});
</script>
</body>
</html>
ÐÑÐµÐ½Ñ Ð²Ð°Ð¶Ð½Ð¾, ÑÑо внеÑний код ÑÑÐ°Ð²Ð¸Ñ Ð¾Ð±ÑабоÑÑик на коÑневой ÑлеменÑ, но не на внÑÑÑенние ÑлеменÑÑ Ð¼ÐµÐ½Ñ.
СÑÑого говоÑÑ, он вообÑе не Ð·Ð½Ð°ÐµÑ Ð¿Ñо Ñо, как ÑÑÑÑоено менÑ, еÑÑÑ Ð»Ð¸ Ñам ÑÑÑлки и какие, или Ñам вообÑе вÑÑ Ñеализовано ÑеÑез кнопки.
ÐÐµÐ½Ñ Ð´Ð»Ñ Ð½ÐµÐ³Ð¾ â «ÑÑÑнÑй ÑÑик». ÐоÑневой ÑÐ»ÐµÐ¼ÐµÐ½Ñ â ÑоÑка доÑÑÑпа к его ÑÑнкÑионалÑноÑÑи. СобÑÑие â не Ñо, коÑоÑое пÑоизоÑло на ÑÑÑлке, а «пеÑеÑабоÑаннÑй ваÑианÑ», инÑеÑпÑеÑаÑÐ¸Ñ Ð´ÐµÐ¹ÑÑÐ²Ð¸Ñ Ñо ÑÑоÑÐ¾Ð½Ñ Ð¼ÐµÐ½Ñ.
Такое пÑавило позволÑÐµÑ Ð½Ð°Ð¼ не опаÑаÑÑÑÑ Ð¿Ñоблем пÑи опÑимизаÑии, ÑаÑÑиÑении и даже полной пеÑеделке DOM-ÑÑÑÑкÑÑÑÑ Ð¼ÐµÐ½Ñ. ÐÐ¾Ð»Ñ ÑкоÑо ÑобÑÑÐ¸Ñ Ð¸ меÑÐ¾Ð´Ñ ÑÐ¾Ñ ÑанÑÑÑÑÑ, внеÑний код бÑÐ´ÐµÑ ÑабоÑаÑÑ ÐºÐ°Ðº пÑежде.
ÐÑÑ Ñаз â внеÑний код не Ð¸Ð¼ÐµÐµÑ Ð¿Ñава залезаÑÑ Ð²Ð½ÑÑÑÑ DOM-ÑÑÑÑкÑÑÑÑ Ð¼ÐµÐ½Ñ, ÑÑавиÑÑ Ñам обÑабоÑÑики и Ñак далее.
ÐÑого
ÐÐ»Ñ Ñого, ÑÑÐ¾Ð±Ñ Ð²Ð½ÐµÑний код мог ÑзнаваÑÑ Ð¾ важнÑÑ ÑобÑÑиÑÑ , пÑоизоÑедÑÐ¸Ñ Ð²Ð½ÑÑÑи компоненÑа, иÑполÑзÑÑÑÑÑ:
- ÐолбÑки â ÑÑнкÑии, коÑоÑÑе пеÑедаÑÑÑÑ Â«ÑнаÑÑжи» пÑи Ñоздании компоненÑа, и коÑоÑÑе он обÑзÑеÑÑÑ Ð²ÑзваÑÑ Ð¿Ñи наÑÑÑплении ÑобÑÑий.
- СобÑÑÐ¸Ñ â ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ Ð³ÐµÐ½ÐµÑиÑÑÐµÑ Ð¸Ñ
на коÑневом ÑлеменÑе пÑи помоÑи
dispatchEvent
, а внеÑний код ÑÑÐ°Ð²Ð¸Ñ Ð¾Ð±ÑабоÑÑики пÑи помоÑиaddEventListener
. Такие ÑобÑÑÐ¸Ñ Ð²ÑплÑваÑÑ, еÑли Ñказан Ñлагbubbles
, поÑÑÐ¾Ð¼Ñ Ñ Ð½Ð¸Ð¼Ð¸ можно иÑполÑзоваÑÑ Ð´ÐµÐ»ÐµÐ³Ð¸Ñование.
ÐомменÑаÑии
<code>
, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>
, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)