使用 vanilla JS 切换 HTML 按钮的基于文本的数据属性,而不影响嵌套图标

Toggle text-based data attributes of HTML buttons without affecting nested icons using vanilla JS

提问人:Mike Hermary 提问时间:11/2/2023 更新时间:11/4/2023 访问量:37

问:

我使用以下箭头函数在两个 HTML 按钮上切换属性、类和数据属性。

第一个按钮包含文本,第二个按钮仅包含嵌套的 SVG 图标。

文本交换适用于带有文本的按钮,但在单击时会从按钮中删除嵌套的 SVG 图标。

如何修改功能以保持基于文本的按钮上的文本交换,即使单击另一个按钮并使 SVG 图标保持不变。

window.addEventListener("DOMContentLoaded", function() {
  const navigation = (() => {
    const buttons = document.querySelectorAll("[data-nav-toggle]");
    const containers = document.querySelectorAll("[data-nav-container]");

    if (!buttons) return;

    function menuToggleHandler() {
      buttons.forEach((button) => {
        button.addEventListener("click", toggleContainer);
      });
    }

    function toggleContainer(e) {
      e.stopPropagation();
      toggleButtonAttributes(e.target);
      toggleContainers(e.target);
    }

    function toggleButtonAttributes(button) {
      button.classList.toggle("is-active");

      const expandedValue = button.getAttribute("aria-expanded");
      const setValue = expandedValue === "true" ? "false" : "true";
      button.setAttribute("aria-expanded", setValue);

      const originalValue = button.getAttribute("data-text-original");
      const swapValue = button.getAttribute("data-text-swap");

      if (swapValue === button.innerText) {
        button.innerText = button.getAttribute("data-text-original");
      } else {
        button.setAttribute("data-text-original", button.innerText);
        button.innerText = button.getAttribute("data-text-swap");
      }

      document.querySelectorAll(".is-active").forEach(function(buttonActive) {
        if (buttonActive !== button) {
          buttonActive.classList.remove("is-active");
          buttonActive.setAttribute("aria-expanded", false);
          if (swapValue === buttonActive.innerText) {
            buttonActive.innerText = buttonActive.getAttribute(
              "data-text-original"
            );
          }
        }
      });
    }

    function toggleContainers(button) {
      const parent = button
        .closest("nav")
        .querySelector("[data-nav-container]");
      parent.classList.toggle("is-expanded");
      document
        .querySelectorAll(".is-expanded")
        .forEach(function(parentExpanded) {
          if (parentExpanded !== parent) {
            parentExpanded.classList.remove("is-expanded");
          }
        });
    }

    menuToggleHandler();
  })();
});
button>svg {
  pointer-events: none;
}
<nav aria-label="Main navigation">
  <button class="c-btn c-btn__menu-toggle" aria-expanded="false" aria-haspopup="menu" data-nav-toggle="global-navigation" data-text-swap="Close" data-text-original="Menu">Menu</button>
  <div class="c-navigation" data-nav-container="global-navigation"></div>
</nav>

<nav aria-label="User navigation">
  <button class="c-btn c-btn__user-menu-toggle" aria-expanded="false" aria-haspopup="menu" aria-label="User menu toggle" data-nav-toggle="user-navigation">
    <svg role="img" width="14" height="16" xmlns="http://www.w3.org/2000/svg" aria-labelledby="user-menu-icon">
      <title id="user-menu-icon">
        Title
      </title>
      <path d="M9.5 4a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0zM3 4a4 4 0 1 1 8 0 4 4 0 0 1-8 0zM1.54 14.5h10.92A4.073 4.073 0 0 0 8.427 11H5.572a4.073 4.073 0 0 0-4.031 3.5zM0 15.072A5.57 5.57 0 0 1 5.572 9.5h2.856A5.57 5.57 0 0 1 14 15.072a.928.928 0 0 1-.928.928H.928A.928.928 0 0 1 0 15.072z" fill="#DCD9D6" />
    </svg>
  </button>
  <div class="c-navigation is-expanded" data-nav-container="user-navigation"></div>
</nav>

javascript html dom-events

评论


答:

0赞 Mike Hermary 11/4/2023 #1

我能够通过首先检查按钮元素是否包含纯文本来解决我的问题,方法是将语句放在周围,然后在 buttonActive forEach 函数中创建一个新的 const 并调整文本交换值的 inf 语句。if (button.innerText) {}if (swapValue === button.innerText) {}

我添加的代码是从活动按钮中检索值,然后在语句中使用该值,使用该属性将活动按钮的 innerText 更改回原始值。const activeSwapValue = buttonActive.getAttribute("data-text-swap");data-text-swapif (activeSwapValue === buttonActive.innerText) {}data-text-original

window.addEventListener("DOMContentLoaded", function() {
  const navigation = (() => {
    const buttons = document.querySelectorAll("[data-nav-toggle]");
    const containers = document.querySelectorAll("[data-nav-container]");

    if (!buttons) return;

    function menuToggleHandler() {
      buttons.forEach((button) => {
        button.addEventListener("click", toggleContainer);
      });
    }

    function toggleContainer(e) {
      e.stopPropagation();
      toggleButtonAttributes(e.target);
      toggleContainers(e.target);
    }

    function toggleButtonAttributes(button) {
      button.classList.toggle("is-active");

      const expandedValue = button.getAttribute("aria-expanded");
      const setValue = expandedValue === "true" ? "false" : "true";
      button.setAttribute("aria-expanded", setValue);

      const originalValue = button.getAttribute("data-text-original");
      const swapValue = button.getAttribute("data-text-swap");

      // Added if statement to check if button element contains plain text
      if (button.innerText) {
        if (swapValue === button.innerText) {
          button.innerText = button.getAttribute("data-text-original");
        } else {
          button.setAttribute("data-text-original", button.innerText);
          button.innerText = button.getAttribute("data-text-swap");
        }
      }

      document.querySelectorAll(".is-active").forEach(function(buttonActive) {
        // Added const to retrieve data-text-swap attribute from active button
        const activeSwapValue = buttonActive.getAttribute("data-text-swap");
        if (buttonActive !== button) {
          buttonActive.classList.remove("is-active");
          buttonActive.setAttribute("aria-expanded", false);
          // Changed swapValue to activeSwapValue in the if statement
          if (activeSwapValue === buttonActive.innerText) {
            buttonActive.innerText = buttonActive.getAttribute(
              "data-text-original"
            );
          }
        }
      });
    }

    function toggleContainers(button) {
      const parent = button
        .closest("nav")
        .querySelector("[data-nav-container]");
      parent.classList.toggle("is-expanded");
      document
        .querySelectorAll(".is-expanded")
        .forEach(function(parentExpanded) {
          if (parentExpanded !== parent) {
            parentExpanded.classList.remove("is-expanded");
          }
        });
    }

    menuToggleHandler();
  })();
});
button>svg {
  pointer-events: none;
}
<nav aria-label="Main navigation">
  <button class="c-btn c-btn__menu-toggle" aria-expanded="false" aria-haspopup="menu" data-nav-toggle="global-navigation" data-text-swap="Close" data-text-original="Menu">Menu</button>
  <div class="c-navigation" data-nav-container="global-navigation"></div>
</nav>

<nav aria-label="User navigation">
  <button class="c-btn c-btn__user-menu-toggle" aria-expanded="false" aria-haspopup="menu" aria-label="User menu toggle" data-nav-toggle="user-navigation">
    <svg role="img" width="14" height="16" xmlns="http://www.w3.org/2000/svg" aria-labelledby="user-menu-icon">
      <title id="user-menu-icon">
        Title
      </title>
      <path d="M9.5 4a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0zM3 4a4 4 0 1 1 8 0 4 4 0 0 1-8 0zM1.54 14.5h10.92A4.073 4.073 0 0 0 8.427 11H5.572a4.073 4.073 0 0 0-4.031 3.5zM0 15.072A5.57 5.57 0 0 1 5.572 9.5h2.856A5.57 5.57 0 0 1 14 15.072a.928.928 0 0 1-.928.928H.928A.928.928 0 0 1 0 15.072z" fill="#DCD9D6" />
    </svg>
  </button>
  <div class="c-navigation is-expanded" data-nav-container="user-navigation"></div>
</nav>