This commit is contained in:
Ilya Kantor 2014-10-26 22:10:13 +03:00
parent 06f61d8ce8
commit f301cb744d
2271 changed files with 103162 additions and 0 deletions

View file

@ -0,0 +1 @@
[edit src="solution"/]

View file

@ -0,0 +1 @@
{"name":"menu-nested","plunk":"Y2FobU7onTBDYA3s4CTG"}

View file

@ -0,0 +1,62 @@
/**
* Taken from jQuery UI accordion and fixed, special hoverintent event
* http://benalman.com/news/2010/03/jquery-special-events/
*/
!function($) {
var cfg = {
sensitivity: 9,
interval: 50
};
$.event.special.hoverintent = {
setup: function() {
$(this).on("mouseover", handler);
},
teardown: function() {
$(this).on("mouseover", handler);
},
cfg: cfg
};
function handler(event) {
var self = this,
args = arguments,
target = $(event.target),
cX, cY, pX, pY;
function track(event) {
cX = event.pageX;
cY = event.pageY;
};
pX = event.pageX;
pY = event.pageY;
function clear() {
target.off("mousemove", track).off("mouseout", clear);
clearTimeout(timeout);
}
function handler() {
if((Math.abs(pX - cX) + Math.abs(pY - cY)) < cfg.sensitivity) {
clear();
event.type = "hoverintent";
jQuery.event.simulate("hoverintent", event.target, $.event.fix(event), true);
} else {
pX = cX;
pY = cY;
timeout = setTimeout(handler, cfg.interval);
}
}
var timeout = setTimeout(handler, cfg.interval);
target.mousemove(track).mouseout(clear);
return true;
}
}(jQuery);

View file

@ -0,0 +1,61 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="menu.css">
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="hoverintent.js"></script>
<script src="menu.js"></script>
</head>
<body>
<!-- Разрешить вложенные меню -->
<div class="menu" id="menu">
<a href="#" class="title">Моё меню</a>
<ol>
<li class="has-children">
<a href="#a1">Элемент 1</a>
<ol>
<li><a href="#a1.1">Элемент 1.1</a></li>
<li class="has-children">
<a href="#a1.2">Элемент 1.2</a>
<ol>
<li><a href="#a1.2.1">Элемент 1.2.1</a></li>
<li><a href="#a1.2.2">Элемент 1.2.2</a></li>
</ol>
</li>
<li><a href="#a1.3">Элемент 1.3</a></li>
</ol>
</li>
<li><a href="#a2">Элемент 2</a></li>
<li class="has-children">
<a href="#a3">Элемент 3</a>
<ol>
<li><a href="#a3.1">Элемент 3.1</a></li>
<li><a href="#a3.2">Элемент 3.2</a></li>
<li><a href="#a3.3">Элемент 3.3</a></li>
</ol>
</li>
<li><a href="#a4">Элемент 4</a></li>
<li><a href="#a5">Элемент 5</a></li>
</ol>
</div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor eos.
<script>
var menu = new Menu({
elem: $('#menu')
});
$(menu).on("select", function(e) {
if (!e.value) return;
alert(e.value);
});
</script>
</body>
</html>

View file

@ -0,0 +1 @@
{"name":"menu-nested","plunk":"Y2FobU7onTBDYA3s4CTG"}

View file

@ -0,0 +1,62 @@
/**
* Taken from jQuery UI accordion and fixed, special hoverintent event
* http://benalman.com/news/2010/03/jquery-special-events/
*/
!function($) {
var cfg = {
sensitivity: 9,
interval: 50
};
$.event.special.hoverintent = {
setup: function() {
$(this).on("mouseover", handler);
},
teardown: function() {
$(this).on("mouseover", handler);
},
cfg: cfg
};
function handler(event) {
var self = this,
args = arguments,
target = $(event.target),
cX, cY, pX, pY;
function track(event) {
cX = event.pageX;
cY = event.pageY;
};
pX = event.pageX;
pY = event.pageY;
function clear() {
target.off("mousemove", track).off("mouseout", clear);
clearTimeout(timeout);
}
function handler() {
if((Math.abs(pX - cX) + Math.abs(pY - cY)) < cfg.sensitivity) {
clear();
event.type = "hoverintent";
jQuery.event.simulate("hoverintent", event.target, $.event.fix(event), true);
} else {
pX = cX;
pY = cY;
timeout = setTimeout(handler, cfg.interval);
}
}
var timeout = setTimeout(handler, cfg.interval);
target.mousemove(track).mouseout(clear);
return true;
}
}(jQuery);

View file

@ -0,0 +1,61 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="menu.css">
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="hoverintent.js"></script>
<script src="menu.js"></script>
</head>
<body>
<!-- Разрешить вложенные меню -->
<div class="menu" id="menu">
<a href="#" class="title">Моё меню</a>
<ol>
<li class="has-children">
<a href="#a1">Элемент 1</a>
<ol>
<li><a href="#a1.1">Элемент 1.1</a></li>
<li class="has-children">
<a href="#a1.2">Элемент 1.2</a>
<ol>
<li><a href="#a1.2.1">Элемент 1.2.1</a></li>
<li><a href="#a1.2.2">Элемент 1.2.2</a></li>
</ol>
</li>
<li><a href="#a1.3">Элемент 1.3</a></li>
</ol>
</li>
<li><a href="#a2">Элемент 2</a></li>
<li class="has-children">
<a href="#a3">Элемент 3</a>
<ol>
<li><a href="#a3.1">Элемент 3.1</a></li>
<li><a href="#a3.2">Элемент 3.2</a></li>
<li><a href="#a3.3">Элемент 3.3</a></li>
</ol>
</li>
<li><a href="#a4">Элемент 4</a></li>
<li><a href="#a5">Элемент 5</a></li>
</ol>
</div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor eos.
<script>
var menu = new Menu({
elem: $('#menu')
});
$(menu).on("select", function(e) {
if (!e.value) return;
alert(e.value);
});
</script>
</body>
</html>

View file

@ -0,0 +1,54 @@
.menu::before {
content: '☞';
font-size: 16px;
float: left;
}
.menu.open::before {
content: '☟';
}
.menu a {
text-decoration: none;
color: black;
display: block;
}
.menu > a {
display: inline;
}
.menu a {
padding: 3px 5px;
white-space: nowrap;
}
li.active > a {
background: blue;
color: white;
}
.menu ol {
margin: 0;
padding: 3px 0;
list-style: none;
display: none;
background: #eee;
border: 1px solid black;
position: absolute;
}
.menu.open > ol {
display: block;
margin-left: 16px;
}
.menu li.has-children > a::after {
content: ' >';
}
.menu li.active > ol {
display: block;
}

View file

@ -0,0 +1,131 @@
function Menu(options) {
var elem = options.elem;
var self = this;
var activeLi;
elem.on('click', 'a.title', onTitleClick);
// item clicks are all inside ol
elem.on('click', 'ol a', onLiClick);
// I use "a" here instead of LI, because hover on LI also works if going over it's child container OL
// child container may have paddings etc, so the click may happen on that OL, outside of this item
elem.on('hoverintent', 'ol a', onLiHoverIntent);
// ----------------------
function onLiClick(e) {
close();
var li = $(e.currentTarget).closest('li');
$(self).triggerHandler({
type: 'select',
value: getLiValue(li)
});
return false;
}
function onTitleClick() {
if (elem.hasClass('open')) {
close();
} else {
open();
}
}
function onLiHoverIntent(e) {
var li = $(this).closest('li');
if (isActiveLi(li)) return;
activateLi(li);
openChildren();
}
// ----------------------
function isActiveLi(li) {
if (activeLi && activeLi[0] == li[0]) return true;
return false;
}
function close(argument) {
elem.removeClass('open');
if (activeLi) {
activeLi.parentsUntil(elem).andSelf().removeClass('active');
activeLi = null;
}
$(document).off('.menu-nested');
}
function getLiValue(li) {
if (!li.length) return null;
return li.children('a').attr('href').slice(2);
}
function open() {
elem.addClass('open');
// TODO: close menu on document click outside of it
$(document).on('click.menu-nested', function(e) {
if ( $(e.target).closest(elem).length ) return;
close();
});
}
function getParentLi(li) {
return li.parent().parent();
}
function activateLi(li) {
if (activeLi && getParentLi(li)[0] != activeLi[0]) { // if (new li is not child of activeLi)
// not a child item, then need to close currently active ones
// collapse parents of last active until container of new element
collapseActiveUntil(li);
}
activeLi = li;
activeLi.addClass("active");
}
function collapseActiveUntil(li) {
var el = activeLi;
for(;;) {
el.removeClass('active');
if (el[0].parentNode == li[0].parentNode) break;
el = getParentLi(el);
}
}
function openChildren() {
var subcontainer = activeLi.children('ol');
if (!subcontainer.length) return;
// show children
var left = activeLi.width(); // to the right of the parent
var top = activeLi.position().top; // at same height as current parent
// lift it up, so that first subchild will be aligned with the parent
top -= subcontainer.children('li').position().top;
top -= subcontainer.prop('clientTop')
subcontainer.css({
left: left,
top: top
})
}
}

View file

@ -0,0 +1,54 @@
.menu::before {
content: '☞';
font-size: 16px;
float: left;
}
.menu.open::before {
content: '☟';
}
.menu a {
text-decoration: none;
color: black;
display: block;
}
.menu > a {
display: inline;
}
.menu a {
padding: 3px 5px;
white-space: nowrap;
}
li.active > a {
background: blue;
color: white;
}
.menu ol {
margin: 0;
padding: 3px 0;
list-style: none;
display: none;
background: #eee;
border: 1px solid black;
position: absolute;
}
.menu.open > ol {
display: block;
margin-left: 16px;
}
.menu li.has-children > a::after {
content: ' >';
}
.menu li.active > ol {
display: block;
}

View file

@ -0,0 +1,131 @@
function Menu(options) {
var elem = options.elem;
var self = this;
var activeLi;
elem.on('click', 'a.title', onTitleClick);
// item clicks are all inside ol
elem.on('click', 'ol a', onLiClick);
// I use "a" here instead of LI, because hover on LI also works if going over it's child container OL
// child container may have paddings etc, so the click may happen on that OL, outside of this item
elem.on('hoverintent', 'ol a', onLiHoverIntent);
// ----------------------
function onLiClick(e) {
close();
var li = $(e.currentTarget).closest('li');
$(self).triggerHandler({
type: 'select',
value: getLiValue(li)
});
return false;
}
function onTitleClick() {
if (elem.hasClass('open')) {
close();
} else {
open();
}
}
function onLiHoverIntent(e) {
var li = $(this).closest('li');
if (isActiveLi(li)) return;
activateLi(li);
openChildren();
}
// ----------------------
function isActiveLi(li) {
if (activeLi && activeLi[0] == li[0]) return true;
return false;
}
function close(argument) {
elem.removeClass('open');
if (activeLi) {
activeLi.parentsUntil(elem).andSelf().removeClass('active');
activeLi = null;
}
$(document).off('.menu-nested');
}
function getLiValue(li) {
if (!li.length) return null;
return li.children('a').attr('href').slice(2);
}
function open() {
elem.addClass('open');
// TODO: close menu on document click outside of it
$(document).on('click.menu-nested', function(e) {
if ( $(e.target).closest(elem).length ) return;
close();
});
}
function getParentLi(li) {
return li.parent().parent();
}
function activateLi(li) {
if (activeLi && getParentLi(li)[0] != activeLi[0]) { // if (new li is not child of activeLi)
// not a child item, then need to close currently active ones
// collapse parents of last active until container of new element
collapseActiveUntil(li);
}
activeLi = li;
activeLi.addClass("active");
}
function collapseActiveUntil(li) {
var el = activeLi;
for(;;) {
el.removeClass('active');
if (el[0].parentNode == li[0].parentNode) break;
el = getParentLi(el);
}
}
function openChildren() {
var subcontainer = activeLi.children('ol');
if (!subcontainer.length) return;
// show children
var left = activeLi.width(); // to the right of the parent
var top = activeLi.position().top; // at same height as current parent
// lift it up, so that first subchild will be aligned with the parent
top -= subcontainer.children('li').position().top;
top -= subcontainer.prop('clientTop')
subcontainer.css({
left: left,
top: top
})
}
}

View file

@ -0,0 +1,12 @@
# Вложенное меню с выпаданием по клику
[importance 4]
Создайте меню с выпаданием по клику и раскрытием внутренних пунктов при наведении.
Демо:
[iframe src="solution" height=200 border=1]
При выборе -- событие `select` с выбранным значением.
HTML/CSS -- на ваш вкус.