Немного переоснащения должно сработать.
Прежде всего, вы должны использовать CSS, чтобы скрыть подменю, так как я думаю, что это должно быть по умолчанию.Кроме того, вы должны реализовать этап инициализации, который запускается на DOMContentReady , чтобы настроить инфраструктуру поддержки.Таким образом вы избавляете себя от хлопот, постоянно подключая и удаляя прослушиватели событий.Вы хотите присоединить прослушиватель событий, который проверяет наличие событий мыши, к элементу, открывающему здесь подменю.Вам также понадобится флаг, который указывает, открыто ли меню, чтобы обработчик мог реагировать соответствующим образом (таким образом вы избавляете себя от проблемы с «переключателем» и «лайками»), плюс вы сразу получаете состояние меню под рукой, если вам нужноделать какие-либо дополнительные проверки).Простая переменная должна сработать.Плюс: если меню скрыто, оно вообще не получает никаких событий.
Если вас интересует размер окна, я бы рекомендовал вам переключиться на window.innerWidth
/ window.innerHeight
вместо этого, потому что это числовое значение, которое вы получаете и которое можно легко сравнить с минимальным требуемым размером.Просто прикрепите обработчик события изменения размера к окну, которое выполняет эту проверку, и все готово.Если размер окна становится меньше вашего минимума, просто принудительно сверните меню.
До свертывания меню, если пользователь щелкает в любом месте документа, присоединение слушателя событий, ищущего щелчки мыши / нажатия клавиш, кЗдесь объект документа добивается цели (установите для стадии всплывающего окна значение true , чтобы перехватить событие, прежде чем что-либо еще может произойти).
Свернуть меню при нажатии на элемент меню можно наилучшим образом, еслиприсоединение обработчика событий к каждому отдельному пункту меню (наилучшее место - ссылки внутри меню - установите для всплывающей подсказки значение false ), которое просто закрывает меню.
РЕДАКТИРОВАТЬ:
Я немного поработал, взяв ваш HTML и CSS, и это то, что я придумал (обратите внимание, что я также преобразовал файл в XHTML - но это ваше делохотите ли вы сделать это или нет):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en">
<head>
<title>Submenu Test Case</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style type="text/css">
body {
background-color: black;
color: white;
}
aside {
width: 15em;
float: left;
}
nav ul {
position: relative;
text-align: left;
list-style: none; /* Kills any list decoration */
}
nav ul ul {
position: relative;
left: 0;
}
nav a {
text-decoration: none;
}
/* This ARIA attribute can greatly improve accessibility and can also be used
to actually hide elements just by tying "display: none;" to it... */
[aria-hidden="true"] {
display: none;
}
.menu a {
color: white;
}
.menu a:hover, .menu span:hover {
background-color: white;
color: black;
}
/* Style definitions that override settings for mobile devices go here! */
@media screen and (min-width: 480px)
{
/* Reposition the submenu if it's on a sufficiently wide window... */
nav ul li > ul {
position: absolute;
margin-top: -1.2em;
left: 7em;
}
}
</style>
<script type="application/javascript">
/* <![CDATA[ */
var submenu_open = false; // Has the submenu been opened?
var need_mobile = false; // Are we on a narrow window?
// Takes care of hiding and showing the submenu
function ToggleMenu(p_event)
{
// Do not activate e. g. on a right click...
if(p_event.button != 0)
return;
if(submenu_open)
{
// If the submenu has previously been open, close it (and adjust the
// controlling menu item if necessary)
document.getElementById('sub1').setAttribute('aria-hidden', 'true');
if(window.innerWidth < 480)
document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
}
else
{
// If the submenu has previously been closed, open it (and adjust the
// controlling menu item if necessary)
document.getElementById('sub1').setAttribute('aria-hidden', 'false');
if(window.innerWidth < 480)
document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▲';
}
// This prevents the document's root node (i. e. the document object) from
// seeing the event when clicking on the superordinate item for the submenu...
p_event.stopPropagation();
submenu_open = !submenu_open;
}
// Triggered upon clicking anywhere inside of the document...
function CloseMenu(p_event)
{
if(!submenu_open)
return;
document.getElementById('sub1').setAttribute('aria-hidden', 'true');
if(window.innerWidth < 480)
document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
submenu_open = false;
}
function CheckWindowSize(p_event)
{
if(window.innerWidth < 480)
{
if(need_mobile)
return;
// On a mobile device, insert the submenu into the main one...
if(submenu_open)
document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▲';
else
document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
}
else
{
if(!need_mobile)
return;
// If the window is wide enough, we can display the submenu next to the main
// one...
document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ►';
}
need_mobile = !need_mobile;
}
// Initialization sequence (adds a few event handlers)
document.addEventListener('DOMContentLoaded', function (p_event) {
document.getElementById('sub-item1').addEventListener('click', ToggleMenu, false);
window.addEventListener('resize', CheckWindowSize, false);
document.addEventListener('click', CloseMenu, false);
// If we are on a mobile device, adjust the text of the menu item.
if(window.innerWidth < 480)
{
need_mobile = true;
document.getElementById('sub-item1').childNodes[0].childNodes[0].data = 'Portfolio ▼';
}
}, false);
/* ]]> */
</script>
</head>
<body>
<header><h1>Submenu Test Case</h1></header>
<aside>
<nav class="menu">
<ul>
<li class="menuItem"><a href="javascript:alert('\'Home\' triggered!');">Home</a></li>
<li class="menuItem"><a href="javascript:alert('\'About\' triggered!');">About</a></li>
<!-- Assume normal operation here (window width >= 480 pixels) so the
text is set accordingly...
Please note that I have removed some extraneous elements and
attributes. -->
<li class="submenu" id="sub-item1"><span>Portfolio ►</span>
<ul id="sub1" aria-hidden="true">
<li><a href="javascript:alert('\'Landscape\' triggered!');">Landscape</a></li>
<li><a href="javascript:alert('\'Architecture\' triggered!');">Architecture</a></li>
<li><a href="javascript:alert('\'Animal\' triggered!');">Animal</a></li>
<li><a href="javascript:alert('\'Other\' triggered!');">Other</a></li>
</ul>
</li>
<li class="menuItem"><a href="javascript:alert('\'Information\' triggered!');">Information</a></li>
<li class="menuItem"><a href="javascript:alert('\'Contact\' triggered!');">Contact</a></li>
</ul>
</nav>
</aside>
<main />
</body>
</html>
Пожалуйста, смотрите аннотации в (X) HTML для деталей о том, что происходите.Работая над этим, я обнаружил, что могу значительно упростить метод, который я упомянул еще раз, поэтому он сводится к трем событиям:
- Resize: переключает расположение меню, когда ширина окна подрезаетопределенный порог
- Щелчок мышью по пункту меню: открывает или закрывает подменю
- Щелчок мышью в любом месте: закрывает меню
Что касается ваших вопросов ...
ad 1 .: Я немного переоборудовал предоставленный вами CSS.Я отбросил некоторые определения и связал скрытие любых элементов с атрибутом aria-hidden
(если установлено значение true, элемент не отображается).Сказанное attrtibute также очень помогает в улучшении доступности.В этом примере, если вы не видите его на экране, программа чтения с экрана также не будет его отображать.
ad 2 .: Это довольно просто настроить.Просто включите document.addEventListener('DOMContentLoaded', function (p_event) { }, false);
в основной путь выполнения и добавьте в функцию все, что нужно настроить.Это особенно важно для так называемого ненавязчивого JavaScript (т. Е. JavaScript, который сам прикрепляет к документу хуки, а не жестко кодирует их в (X) HTML).
ad 3 .: Вы можетесделать это с помощью простой переменной (логическое значение, которое указывает, открыто ли меню).Затем вы можете быстро проверить состояние меню, не запрашивая DOM.
ad 4 .: Это чрезвычайно утомительно и в лучшем случае дорого, поэтому обработчики присоединяются только один раз.Остальное должно зависеть от логики управления, если какие-либо события игнорируются (просто возвращаясь, если не выполнены определенные условия).
ad 5 .: Требуется обработчик того типа, который вы реализовали, но я немного упростил его, чтобы избежать вызова DOM там, где он не нужен (он прибегает к вышеупомянутому флагу), плюс он также учитывает окноширина для корректировки текста.
ad 6 .: innerWidth
/ innerHeight
- это числовые значения, которые могут быть легко возвращены DOM.Я не знаю, каков ваш метод, но, по моему мнению, он кажется немного дорогим, плюс, когда вы сохраняете значение в переменной, вы можете выполнить несколько проверок (например, если вам нужно проверить разные ширины / высоты).), что просто требует простых сравнений.Ваш подход потребует, чтобы вы сбросили условие соответствия.
ad 7 .: Мне нужно исправить себя здесь, потому что, пережевывая вашу проблему, я обнаружил, что все (то есть третий параметр в addEventListener
) должно бытьустановите на false .В противном случае порядок исполнения нарушен, или некоторые ссылки не видят событие в первую очередь.
ad 8 .: Оказалось, что также не нужно.Я получил свой первоначальный ответ из контекстного меню, которое я реализовал в JavaScript, но из-за его природы мне пришлось прибегнуть к этим обработчикам, чтобы закрыть его.Здесь все немного по-другому, поэтому все можно упростить, просто опуская эти обработчики.
ad 9 .: Вы на самом деле выбрали лучший путь здесь.При реализации навигации я также использую этот подход.
Я надеюсь, что смогу превратить некоторые из ваших вопросительных знаков в восклицательные знаки.Однако, если у вас все еще есть вопросы, пожалуйста, задавайте.Нет ничего хуже, чем вопросы, оставшиеся без ответа.