пузыриться в простом меню, используя HTML, CSS и JavaScript - PullRequest
0 голосов
/ 25 августа 2018

В приведенном ниже коде для простого меню, если подменю развернуто / показано, то мне нужно, чтобы подменю свернулось при следующих условиях:

  1. Когда главное меню или подменю-меню LI щелкают
  2. Когда область просмотра больше определенной минимальной ширины
  3. Когда щелчок происходит где-либо еще на экране

Номер 2 работает и номер1 работает до тех пор, пока я не объявляю addEventListener для элемента mainMenuID.По какой-то причине, когда я добавляю этот прослушиватель событий для любого элемента mainMenuID, он имеет приоритет над чем-либо еще, таким образом, щелкая элемент главного меню, кроме «Портфолио» или его элементов подменю, раскрывает и сворачивает меню, но нажимает на submenuID илиulID ничего не делает.Как только я удаляю список событий для mainMenuID, нажимая на submenuID или ulID, можно развернуть / свернуть подменю.

Второй вопрос: как добавить прослушиватель события click для остальной части страницы, чтобыесли отображается подменю, щелчок сворачивает подменю.

Спасибо!

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>

<style media="screen">
body{
  background-color: black;
}

nav ul {
    position: relative;
    text-align: left;
}

nav ul li {
    display: block;  /* WHAT????  REMOVING THIS ACTUALLY MAKES TEXT-DECORATIONS REAPPEAR.... WHY???*/
}

nav a {
    text-decoration: none;
}

/* Hide dropdowns by default */
nav ul ul {
    position: relative;
    display: none;
    left: 0;
}

/* Display Dropdowns on Click */
    nav ul li.showSubmenu > ul {
    display: block;
    position: relative;
    width: 30%;
}

.menu a {
    text-decoration-style: none;
    text-decoration: none;
    background-color: black;
    color: white;
}

.menu a:hover{
    background-color: white;
    color: black;
}
</style>

<body>
    <nav id="mainMenuID" class="menu">
        <ul>
            <li class="menuItem"><a href="#">Home</a></li>
            <li class="menuItem"><a href="#">About</a></li>
            <li id="submenuItem" class="submenu_class"><a href="#"><span id="submenuID">Portfolio ▼</span></a>
            <ul id="ulID">
                <li><a href="#">Landscape</a></li>
                <li><a href="#">Architecture</a></li>
                <li><a href="#">Animal</a></li>
                <li><a href="#">Other</a></li>
            </ul>
            </li>
            <li class="menuItem"><a href="#">Information</a></li>
            <li class="menuItem"><a href="#">Contact</a></li>
       </ul>
    </nav>


<script>
function openSubmenu() {
    if (document.getElementsByClassName('showSubmenu').length == 0){
      document.getElementById("submenuItem").classList.toggle("showSubmenu");
      document.getElementById("submenuID").textContent = "Portfolio ▲";
    } else {
      document.getElementById("submenuItem").classList.toggle("showSubmenu");
      document.getElementById("submenuID").textContent = "Portfolio ▼";
    }
}


function resetSubmenu() {
    var submenuElements = document.getElementsByClassName('submenu_class showSubmenu');
    for (var i = 0; i < submenuElements.length; i++) {
    submenuElements[i].setAttribute('class', 'submenu_class');
    document.getElementById("submenuID").textContent = "Portfolio ►";
    }
}

function screenWidthFunction(x) {
    if (x.matches) {//if it's a narrow screen
        document.getElementById("submenuID").textContent = "Portfolio ▼";
        document.getElementById("ulID").addEventListener("click", openSubmenu, true);
        document.getElementById("submenuID").addEventListener("click", openSubmenu, true);
        document.getElementById("mainMenuID").addEventListener("click", openSubmenu, true);
    } else {
        resetSubmenu();
        document.getElementById("submenuID").textContent = "Portfolio ►";
        document.getElementById("ulID").removeEventListener("click", openSubmenu);
        document.getElementById("submenuID").removeEventListener("click", openSubmenu);
        document.getElementById("mainMenuID").removeEventListener("click", openSubmenu);
      }
   }

    var x = window.matchMedia("(max-width: 480px)");
    screenWidthFunction(x);
    x.addListener(screenWidthFunction);
</script>

<> Привет, Робиду, у меня работает это менюиспользуя только CSS, но я был вынужден использовать JS для некоторых его функций, что остановило работу большей части CSS.Так что на ледниковых скоростях я пытался выучить JS на лету.Хотите верьте, хотите нет, учитесь на видео, W3Schools и т. Д.Я писал, переработал и внедрил это меню в течение большей части 5 или 6 недель.Абсолютно смешно.Кажется, вы понимаете и хорошо знакомы с JS.Мне потребовалось бы еще много недель, чтобы выяснить, как реализовать все ваши советы, и по пути я, скорее всего, разрушу то, что уже работает.Можно ли как-нибудь попросить вас избавить меня от этой боли, предоставив мне либо фрагменты кода, где они должны идти, либо просто более конкретные советы.Ниже приведены фрагменты вашего текста, по которым у меня возникают вопросы (из-за ограничений на количество символов в комментариях я должен разделить их на несколько комментариев):

  1. «… вам следует использовать CSS дляскрыть подменю, так как я думаю, что это будет по умолчанию ». q.Да, это по умолчанию.Я думал, что уже делал это в «nav ul li.showSubmenu> ul {…}» в моем CSS.
  2. «… реализую этап инициализации, который запускается на DOMContentReady для настройки инфраструктуры поддержки». Q.Это, я бы никогда не знал, как самому разобраться.
  3. «… нужен флаг, который указывает, открыто ли меню, чтобы обработчик мог реагировать соответствующим образом… избавьте себя от проблемы с« переключением »…состояние меню под рукой ... »q.Я думал, что «переключение» класса «showSubmenu» служит таким «флагом».Как и где я мог бы использовать переменную вместо переключателя?
  4. «… избавьте себя… многократно присоединяя и удаляя прослушиватели событий»., А затем «Если меню скрыто, оно не получает никакогособытия вообще ».Вы хотите назначить слушателям переменные в любом месте кода JS?Разве мне не нужно было бы ссылаться на эти переменные в тех же местах, где я сейчас использую прослушиватели, и таким же образом?
  5. «… присоединить прослушиватель событий, который проверяет события мыши, к элементу, открывающемуподменю здесь. ”q.Вы имеете в виду, кроме слушателя у меня на id = «submenuID»?Что может отличаться от того, что вы советуете?
  6. «… присоединить обработчик события изменения размера к окну, которое выполняет эту проверку ...» q.Я обрезал код ширины экрана из примеров W3.Как / где будет отличаться использование innerWidth / innerHeight?
  7. «… установите для стадии барботирования значение true, чтобы перехватить событие».кв.Я не смог найти никакого объяснения барботирующим работам, если вы примените одинкак к элементам в системе меню, так и к другим элементам на странице и т. д. Я понимаю основы, но не могу понять, почему не работало это простое меню, когда использовались все три слушателя.
  8. «Свертывание меню при нажатии на элемент меню… прикрепить событие обработчик каждого отдельного пункта меню (лучшее место - ссылки внутри меню ». кв. Придется ли мне присваивать ID каждому LI?

  9. Общий вопрос: я использовал элемент NAV с UL и LI, потому что Мне сказали, что это самый доступный для программ чтения с экрана. Есть ли лучший способ сделать то, что я пытаюсь сделать с этим меню, или я сортирую на правильном пути?

Я искренне извиняюсь за все последующие вопросы / разъяснения, но я искренне крутлю на этом свои колеса, кругами, в бурном море, ношу повязку на глаза…. и любая другая аналогия, которую вы можете добавить в смесь.

Я был бы очень признателен за любое конкретное кодирование, которое вы можете предоставить, чтобы продемонстрировать специфику вашего совета. (или даже укажите на примеры, чтобы я мог их выяснить).

1 Ответ

0 голосов
/ 25 августа 2018

Немного переоснащения должно сработать.

Прежде всего, вы должны использовать 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 .: Вы на самом деле выбрали лучший путь здесь.При реализации навигации я также использую этот подход.

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

...