同一页面上的多个动态选项卡式组件

Multiple Dynamic Tabbed Components on Same Page

几天来我一直在为这个任务而苦苦挣扎。此代码符合 ADA 标准,只要用户每页只使用一个代码,它就可以完美运行。我已经获取了组件的所有实例并循环遍历它们,然后执行所有功能,但到目前为止我没有尝试过任何工作。我也尝试过使用类似于此 post() 的逻辑,但仍然没有成功。

有人能帮帮我吗?

这是我的 HTML:

<!-- First Instance -->
<div class="tabbed-container">
  <div class="tabbed">
  
  <ul>
    <li>
      <a href="#section1">Section 1</a>
    </li>
    <li>
      <a href="#section2">Section 2</a>
    </li>
    <li>
       <a href="#section3">Section 3</a>
    </li>
     <li>
       <a href="#section4">Section 4</a>
    </li>
  </ul>
  
  <section id="section1">
    <h2>Section 1</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, tortor nec pharetra ultricies, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
  </section>
  <section id="section2">
    <h2>Section 2</h2>
    <p>Nullam at diam nec arcu suscipit auctor non a erat. Sed et magna semper, eleifend magna non, facilisis nisl. Proin et est et lorem dictum finibus ut nec turpis. Aenean nisi tortor, euismod a mauris a, mattis scelerisque tortor. Sed dolor risus, varius a nibh id, condimentum lacinia est. In lacinia cursus odio a aliquam. Curabitur tortor magna, laoreet ut rhoncus at, sodales consequat tellus.</p>
  </section>
  <section id="section3">
    <h2>Section 3</h2>
    <p>Phasellus ac tristique orci. Nulla maximus <a href="">justo nec dignissim consequat</a>. Sed vehicula diam sit amet mi efficitur vehicula in in nisl. Aliquam erat volutpat. Suspendisse lorem turpis, accumsan consequat consectetur gravida, <a href="#">pellentesque ac ante</a>. Aliquam in commodo ligula, sit amet mollis neque. Vestibulum at facilisis massa.</p>
  </section>
  <section id="section4">
    <h2>Section 4</h2>
    <p>Nam luctus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, tortor nec pharetra ultricies, ante erat imperdiet velit, nec laoreet enim lacus a velit. </p>
  </section>
</div>
</div>

<!-- Second Instance -->
<div class="tabbed-container">
  <div class="tabbed">
  
  <ul>
    <li>
      <a href="#section1">Section 1</a>
    </li>
    <li>
      <a href="#section2">Section 2</a>
    </li>
    <li>
       <a href="#section3">Section 3</a>
    </li>
     <li>
       <a href="#section4">Section 4</a>
    </li>
  </ul>
  
  <section id="section1">
    <h2>Section 1</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, tortor nec pharetra ultricies, ante erat imperdiet velit, nec laoreet enim lacus a velit. <a href="#">Nam luctus</a>, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan.</p>
  </section>
  <section id="section2">
    <h2>Section 2</h2>
    <p>Nullam at diam nec arcu suscipit auctor non a erat. Sed et magna semper, eleifend magna non, facilisis nisl. Proin et est et lorem dictum finibus ut nec turpis. Aenean nisi tortor, euismod a mauris a, mattis scelerisque tortor. Sed dolor risus, varius a nibh id, condimentum lacinia est. In lacinia cursus odio a aliquam. Curabitur tortor magna, laoreet ut rhoncus at, sodales consequat tellus.</p>
  </section>
  <section id="section3">
    <h2>Section 3</h2>
    <p>Phasellus ac tristique orci. Nulla maximus <a href="">justo nec dignissim consequat</a>. Sed vehicula diam sit amet mi efficitur vehicula in in nisl. Aliquam erat volutpat. Suspendisse lorem turpis, accumsan consequat consectetur gravida, <a href="#">pellentesque ac ante</a>. Aliquam in commodo ligula, sit amet mollis neque. Vestibulum at facilisis massa.</p>
  </section>
  <section id="section4">
    <h2>Section 4</h2>
    <p>Nam luctus, enim in interdum condimentum, nisl diam iaculis lorem, vel volutpat mi leo sit amet lectus. Praesent non odio bibendum magna bibendum accumsan. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam euismod, tortor nec pharetra ultricies, ante erat imperdiet velit, nec laoreet enim lacus a velit. </p>
  </section>
</div>
</div>

这是我的 JavaScript:

(function () {
  const tabbedInstances = Array.from(
    document.querySelectorAll(".tabbed-container")
  );
  console.log(tabbedInstances);

  tabbedInstances.forEach((item) => {
    // Get relevant elements and collections
    const tabbed = document.querySelector(".tabbed");
    const tablist = tabbed.querySelector("ul");
    const tabs = tablist.querySelectorAll("a");
    const panels = tabbed.querySelectorAll('[id^="section"]');
    console.log(panels);

    // The tab switching function
    const switchTab = (oldTab, newTab) => {
      newTab.focus();
      // Make the active tab focusable by the user (Tab key)
      newTab.removeAttribute("tabindex");
      // Set the selected state
      newTab.setAttribute("aria-selected", "true");
      oldTab.removeAttribute("aria-selected");
      oldTab.setAttribute("tabindex", "-1");
      // Get the indices of the new and old tabs to find the correct
      // tab panels to show and hide
      let index = Array.prototype.indexOf.call(tabs, newTab);
      let oldIndex = Array.prototype.indexOf.call(tabs, oldTab);
      panels[oldIndex].hidden = true;
      panels[index].hidden = false;
    };

    // Add the tablist role to the first <ul> in the .tabbed container
    tablist.setAttribute("role", "tablist");

    // Add semantics are remove user focusability for each tab
    Array.prototype.forEach.call(tabs, (tab, i) => {
      tab.setAttribute("role", "tab");
      tab.setAttribute("id", "tab" + (i + 1));
      tab.setAttribute("tabindex", "-1");
      tab.parentNode.setAttribute("role", "presentation");

      // Handle clicking of tabs for mouse users
      tab.addEventListener("click", (e) => {
        e.preventDefault();
        let currentTab = tablist.querySelector("[aria-selected]");
        if (e.currentTarget !== currentTab) {
          switchTab(currentTab, e.currentTarget);
        }
      });

      // Handle keydown events for keyboard users
      tab.addEventListener("keydown", (e) => {
        // Get the index of the current tab in the tabs node list
        let index = Array.prototype.indexOf.call(tabs, e.currentTarget);
        // Work out which key the user is pressing and
        // Calculate the new tab's index where appropriate
        let dir =
          e.which === 37
            ? index - 1
            : e.which === 39
            ? index + 1
            : e.which === 40
            ? "down"
            : null;
        if (dir !== null) {
          e.preventDefault();
          // If the down key is pressed, move focus to the open panel,
          // otherwise switch to the adjacent tab
          dir === "down"
            ? panels[i].focus()
            : tabs[dir]
            ? switchTab(e.currentTarget, tabs[dir])
            : void 0;
        }
      });
    });

    // Add tab panel semantics and hide them all
    Array.prototype.forEach.call(panels, (panel, i) => {
      panel.setAttribute("role", "tabpanel");
      panel.setAttribute("tabindex", "-1");
      let id = panel.getAttribute("id");
      panel.setAttribute("aria-labelledby", tabs[i].id);
      panel.hidden = true;
    });

    // Initially activate the first tab and reveal the first tab panel
    tabs[0].removeAttribute("tabindex");
    tabs[0].setAttribute("aria-selected", "true");
    panels[0].hidden = false;
  });
})();

我已经成功地创建了一个可以在一个页面上多次出现的响应式、可访问的选项卡式界面。我希望这可以帮助别人。 [响应式无障碍选项卡 UI][1]

JavaScript以下 (HTML & CSS 在 https://codepen.io/Rburrage/pen/BaJjJbJ 的 Codepen link 上)

let dropdownMenus = document.querySelectorAll(".dropdown-content");
let dropdownTriggers = document.querySelectorAll(".dropdown-trigger");
let tabLinks = Array.from(
  document.querySelectorAll(".shell-tablinks a")
);

let handleClick = (e) => {
  e.preventDefault();

  let activeTabs = Array.from(
    e.target.parentElement.parentElement.parentElement.querySelectorAll(
      ".active"
    )
  );
  activeTabs.forEach((e) => {
    e.classList.remove("active");
  });

  e.target.parentElement.classList.add("active");

  //Set trigger text to selected dropdown item text
  let button = e.target.parentElement.parentElement.parentElement
    .getElementsByClassName("dropdown-trigger")[0]
    .querySelector("span");
  button.textContent = e.target.parentElement.textContent;

  //Set current tabpanel class to active
  let singleTabPanel = document.querySelector(
    `div.shell-tab-content[data-id*="${e.target.parentElement.dataset.id}"]`
  );
  singleTabPanel.classList.add("active");
};

tabLinks.forEach((link) => {
  //Set tabindex accordingly in order for the user to be able to tab into each active panel.
  if (link.parentElement.classList.contains("active")) {
    link.setAttribute("tabindex", "0");
  } else {
    link.setAttribute("tabindex", "-1");
  }

  link.addEventListener("click", handleClick, false);

  /*===============================================*/
  // Keyboard users
  /*===============================================*/

  const switchTab = (oldTab, newTab) => {
    // Add focus on tab key
    newTab.focus();

    // Set the selected state
    newTab.removeAttribute("tabindex");
    newTab.setAttribute("aria-selected", "true");
    oldTab.removeAttribute("aria-selected");
    oldTab.setAttribute("tabindex", "-1");
  };

  link.addEventListener("keydown", (e) => {
    e.stopPropagation();

    let tabPanels = Array.from(
      document.querySelectorAll(".shell-tab-content")
    );

    // Get current tab index
    let index = Array.prototype.indexOf.call(tabLinks, e.currentTarget);

    // Arrow Keys - 37 = left arrow, 39 = right arrow, 40 = down arrow
    let dir =
      e.which === 37
        ? index - 1
        : e.which === 39
        ? index + 1
        : e.which === 40
        ? "down"
        : null;

    if (dir !== null) {
      e.preventDefault();
      let i;
      // Down key moves user into current panel
      dir === "down"
        ? tabPanels[i].focus()
        : tabLinks[dir]
        ? switchTab(e.currentTarget, tabLinks[dir])
        : void 0;
    }
  });
});

// Toggle between hiding and showing the dropdown content */
dropdownMenus.forEach((menu, i) => {
  menu = dropdownMenus[i];

  dropdownTriggers[i].addEventListener("click", function () {
    menu.classList.toggle("show");
  });
});

// Phones: Replace "role='tablist' with "role='listbox' and add hasPopup attribute to inform screenreaders of the dropdown behavior"
let setNavAttribute = () => {
  let w = document.documentElement.clientWidth || window.innerWidth;

  if (w <= 591) {
    dropdownMenus.forEach((menu) => {
      menu.setAttribute("role", "listbox");
    });
    dropdownTriggers.forEach((button) => {
      button.setAttribute("aria-haspopup", "true");
    });
  } else {
    dropdownMenus.forEach((menu) => {
      menu.setAttribute("role", "tablist");
    });
    dropdownTriggers.forEach((button) => {
      button.removeAttribute("aria-haspopup", "true");
    });
  }
};
window.addEventListener("resize", setNavAttribute);
window.addEventListener("load", setNavAttribute);

// Close the dropdown menu if the user clicks outside of it
window.onclick = function (event) {
  if (!event.target.matches(".dropdown-trigger")) {
    for (let i = 0; i < dropdownMenus.length; i++) {
      let openDropdown = dropdownMenus[i];
      if (openDropdown.classList.contains("show")) {
        openDropdown.classList.remove("show");
      }
    }
  }
};