Amazon Full View

Automatically expand all collapsed sections, truncated text, and FAQ accordions on Amazon product pages

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Amazon Full View
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Automatically expand all collapsed sections, truncated text, and FAQ accordions on Amazon product pages
// @author       zhaozyu with kiro
// @homepageURL  https://greasyfork.org/en/scripts/580063-amazon-dp-auto-expand-collapsed-sections
// @include      https://www.amazon.*/dp/*
// @include      https://www.amazon.*/*/dp/*
// @include      https://www.amazon.*/gp/product/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  "use strict";

  /**
   * Global capture-phase click interceptor.
   * 1. Blocks Amazon's delegated popover handler from firing on truncated
   *    attribute "See more" links in the product overview area.
   * 2. Blocks programmatic clicks on "詳細はこちら" navigation links.
   */
  document.addEventListener(
    "click",
    (e) => {
      const target = e.target.closest ? e.target.closest("a") : null;
      if (!target) return;

      // Block any click on links containing skip text (e.g. "詳細はこちら")
      const targetText = target.textContent.trim();
      if (SKIP_TEXTS.some((t) => targetText.includes(t))) {
        // Only block programmatic clicks (isTrusted=false), allow real user clicks
        if (!e.isTrusted) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          return;
        }
      }

      // Only intercept links inside product overview truncate areas
      const overviewArea = target.closest(
        "#productOverview_feature_div, #poExpander, .po-expander, " +
        "#productFactsDesktop_Section, .product-facts"
      );
      if (!overviewArea) return;

      // Check if this link is a popover trigger or inside a truncate cell
      const isPopoverTrigger =
        target.hasAttribute("data-a-popover") ||
        target.getAttribute("data-action") === "a-popover";
      const cell = target.closest("td");
      const hasTruncate = cell && cell.querySelector(
        ".a-truncate, .a-truncate-full, .a-truncate-cut"
      );

      if (isPopoverTrigger || hasTruncate) {
        const text = target.textContent.trim();
        const expandTexts = ["表示を増やす", "See more", "Show more", "Read more"];
        if (expandTexts.some((t) => text.includes(t))) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          // Trigger inline expansion instead
          expandTruncatedCellsInline();
        }
      }
    },
    true // capture phase - fires before Amazon's handlers
  );

  /**
   * Expand truncated attribute cells inline instead of opening a popover/modal.
   * Targets the product overview table where fields like "特徴" are cut off
   * with a "表示を増やす" link that normally triggers a popup.
   */
  function expandTruncatedCellsInline() {
    // Target all a-truncate containers in the product overview area
    const truncateContainers = document.querySelectorAll(
      [
        "#productOverview_feature_div .a-truncate",
        "#poExpander .a-truncate",
        ".po-expander .a-truncate",
        "#productFactsDesktop_Section .a-truncate",
        ".product-facts .a-truncate",
        'table.a-normal .a-truncate',
        'table.prodDetTable .a-truncate',
      ].join(",")
    );

    truncateContainers.forEach((container) => {
      // Skip if already processed
      if (container.dataset.inlineExpanded === "true") return;

      const fullTextEl = container.querySelector(".a-truncate-full");
      const cutTextEl = container.querySelector(".a-truncate-cut");

      if (fullTextEl && cutTextEl) {
        // Show the full text element and hide the cut version
        fullTextEl.style.display = "inline";
        fullTextEl.classList.remove("a-offscreen");
        cutTextEl.style.display = "none";

        // Remove the max-height / overflow constraints on the container
        container.style.maxHeight = "none";
        container.style.overflow = "visible";
        container.style.webkitLineClamp = "unset";
        container.classList.remove("a-truncate");

        // Hide the "表示を増やす" / "See more" link
        const seeMoreLink = container.querySelector(
          ".a-truncate-see-more, .a-declarative"
        );
        if (seeMoreLink) {
          seeMoreLink.style.display = "none";
        }

        // Also remove any popover trigger attributes to prevent modal
        const popoverTriggers = container.querySelectorAll(
          '[data-action="a-popover"], [data-a-popover]'
        );
        popoverTriggers.forEach((trigger) => {
          trigger.remove();
        });

        // Remove the "表示を増やす" link entirely from the container
        const seeMoreLinks = container.querySelectorAll(
          'a[href="javascript:void(0)"], .a-truncate-see-more, .a-declarative'
        );
        seeMoreLinks.forEach((link) => {
          link.remove();
        });

        container.dataset.inlineExpanded = "true";
      }
    });

    // Also handle the case where the "See more" link is outside the truncate
    // container but within the same table cell (td)
    const overviewCells = document.querySelectorAll(
      [
        "#productOverview_feature_div td",
        "#poExpander td",
        ".po-expander td",
        "#productFactsDesktop_Section td",
        ".product-facts td",
      ].join(",")
    );

    overviewCells.forEach((cell) => {
      if (cell.dataset.inlineExpanded === "true") return;

      const truncateEl = cell.querySelector(".a-truncate");
      if (!truncateEl) return;

      const fullTextEl = truncateEl.querySelector(".a-truncate-full");
      const cutTextEl = truncateEl.querySelector(".a-truncate-cut");

      if (fullTextEl && cutTextEl) {
        fullTextEl.style.display = "inline";
        fullTextEl.classList.remove("a-offscreen");
        cutTextEl.style.display = "none";

        truncateEl.style.maxHeight = "none";
        truncateEl.style.overflow = "visible";
        truncateEl.classList.remove("a-truncate");

        // Remove any "表示を増やす" popover links from the cell entirely
        const expandLinks = cell.querySelectorAll(
          'a[data-action="a-popover"], a[data-a-popover], .a-truncate-see-more, a[href="javascript:void(0)"]'
        );
        expandLinks.forEach((link) => {
          link.remove();
        });

        cell.dataset.inlineExpanded = "true";
      }
    });
  }

  /**
   * Intercept and block popover triggers for truncated product overview cells.
   * This prevents the modal from appearing even if the link is clicked.
   */
  function blockTruncatePopovers() {
    const popoverLinks = document.querySelectorAll(
      [
        '#productOverview_feature_div a[data-action="a-popover"]',
        '#poExpander a[data-action="a-popover"]',
        '.po-expander a[data-action="a-popover"]',
        '#productFactsDesktop_Section a[data-action="a-popover"]',
        '.product-facts a[data-action="a-popover"]',
      ].join(",")
    );

    popoverLinks.forEach((link) => {
      if (link.dataset.popoverBlocked === "true") return;

      // Check if this link is inside or near a truncate container
      const cell = link.closest("td") || link.closest("tr");
      if (!cell) return;

      const hasTruncate = cell.querySelector(
        ".a-truncate, .a-truncate-full, .a-truncate-cut"
      );
      if (!hasTruncate) return;

      // Block the popover by capturing click and stopping propagation
      link.addEventListener(
        "click",
        (e) => {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();

          // Instead of popover, expand inline
          expandTruncatedCellsInline();
        },
        true
      );

      link.dataset.popoverBlocked = "true";
    });
  }

  /**
   * Text patterns that indicate a link/expander should NOT be auto-expanded.
   * These are navigation or info links, not content expanders.
   */
  const SKIP_TEXTS = ["詳細はこちら", "Details here", "Click here for details"];

  /**
   * Check if an element itself contains skip text.
   * Only checks the element's own direct text, not parent containers,
   * to avoid false positives on nearby expanders.
   */
  function shouldSkipElement(el) {
    // If already marked as skip, return immediately
    if (el.dataset.dpAutoSkip === "true") return true;

    // Check the element's own text content
    const text = el.textContent.trim();
    if (SKIP_TEXTS.some((t) => text.includes(t))) {
      el.dataset.dpAutoSkip = "true";
      return true;
    }

    // Check only the immediate expander content (sibling), not the whole page section
    const expanderContainer = el.closest(".a-expander-container");
    if (expanderContainer) {
      // Only check the expander-content div that is a direct child
      const contentDiv = expanderContainer.querySelector(
        ":scope > .a-expander-content"
      );
      if (contentDiv) {
        const contentText = contentDiv.textContent.trim();
        if (SKIP_TEXTS.some((t) => contentText.includes(t))) {
          el.dataset.dpAutoSkip = "true";
          return true;
        }
      }
    }

    return false;
  }

  /**
   * Track elements we've already clicked to prevent repeated expansion.
   */
  const clickedElements = new WeakSet();

  /**
   * Pre-scan and mark all elements that should never be auto-expanded.
   * This runs before expandAllSections to block "詳細はこちら" etc.
   */
  function markSkipElements() {
    // Find all expander headers/links that contain skip text
    const allExpanders = document.querySelectorAll(
      '.a-expander-header, .a-expander-prompt, .a-expander-partial-collapse-header'
    );
    allExpanders.forEach((el) => {
      const text = el.textContent.trim();
      if (SKIP_TEXTS.some((t) => text.includes(t))) {
        el.dataset.dpAutoSkip = "true";
        clickedElements.add(el);
      }
    });

    // Find ALL links that contain skip text (covers non-expander <a> tags)
    const allLinks = document.querySelectorAll("a");
    allLinks.forEach((link) => {
      const text = link.textContent.trim();
      if (SKIP_TEXTS.some((t) => text.includes(t))) {
        link.dataset.dpAutoSkip = "true";
        clickedElements.add(link);
      }
    });

    // Also find links whose expander-content contains skip text
    const allContainers = document.querySelectorAll(".a-expander-container");
    allContainers.forEach((container) => {
      const contentText = container.textContent;
      if (SKIP_TEXTS.some((t) => contentText.includes(t))) {
        const header = container.querySelector(
          ".a-expander-header, .a-expander-prompt"
        );
        if (header) {
          header.dataset.dpAutoSkip = "true";
          clickedElements.add(header);
        }
      }
    });
  }

  /**
   * Safe click that prevents clicking on skip-marked elements.
   * Allows retry if the element is still collapsed (click didn't take effect).
   * Only permanently blocks elements marked as dpAutoSkip.
   */
  function safeClick(el) {
    if (el.dataset.dpAutoSkip === "true") return false;
    if (clickedElements.has(el)) {
      // If previously clicked, only skip if it's now expanded
      const isExpanded =
        el.getAttribute("aria-expanded") === "true" ||
        el.closest(".a-expander-container")?.classList.contains("a-expander-expanded") ||
        el.closest(".a-expander-partial-collapse-container")?.classList.contains("a-expander-inline-expanded");
      if (isExpanded) return false;
      // Otherwise allow retry
    }
    clickedElements.add(el);
    el.click();
    return true;
  }

  /**
   * Force-expand all A+ FAQ module Q&A panels.
   * Amazon A+ Premium FAQ uses a custom structure:
   *   ul.faq-list > li.faq-block > span[data-faq-question][aria-expanded]
   *   Answer panels are controlled via aria-controls pointing to answer element IDs.
   * The accordion behavior only allows one open at a time.
   * We force all panels open and disable the exclusive behavior.
   */
  function forceExpandAplusAccordions() {
    // Target A+ FAQ question triggers
    const faqQuestions = document.querySelectorAll(
      'span[data-faq-question], [data-faq-question]'
    );

    faqQuestions.forEach((trigger) => {
      const isExpanded = trigger.getAttribute("aria-expanded") === "true";
      if (isExpanded) return;

      // Get the answer element via aria-controls
      const controlsId = trigger.getAttribute("aria-controls");
      const answerEl = controlsId ? document.getElementById(controlsId) : null;

      // Force expand
      trigger.setAttribute("aria-expanded", "true");

      if (answerEl) {
        answerEl.style.display = "block";
        answerEl.style.visibility = "visible";
        answerEl.style.height = "auto";
        answerEl.style.maxHeight = "none";
        answerEl.style.overflow = "visible";
        answerEl.style.opacity = "1";
        answerEl.removeAttribute("hidden");
        answerEl.classList.remove("a-hidden");
      }

      // Also try to find the answer as a sibling
      const parentLi = trigger.closest("li");
      if (parentLi) {
        const answer = parentLi.querySelector(
          ".aplus-answer, .faq-answer, [class*='answer']"
        );
        if (answer) {
          answer.style.display = "block";
          answer.style.visibility = "visible";
          answer.style.height = "auto";
          answer.style.maxHeight = "none";
          answer.style.overflow = "visible";
          answer.style.opacity = "1";
        }
      }
    });

    // Also handle via CSS class on the faq-block list items
    const faqBlocks = document.querySelectorAll(
      "li.faq-block, .faq-block"
    );
    faqBlocks.forEach((block) => {
      block.classList.add("faq-expanded");
      // Show all child elements that might be hidden answers
      const hiddenChildren = block.querySelectorAll(
        '[style*="display: none"], [style*="display:none"], [hidden], .a-hidden'
      );
      hiddenChildren.forEach((child) => {
        child.style.display = "block";
        child.removeAttribute("hidden");
        child.classList.remove("a-hidden");
      });
    });

    // Disable the exclusive accordion behavior by removing event bindings
    // Clone and replace the FAQ list to strip all event listeners
    const faqLists = document.querySelectorAll("ul.faq-list");
    faqLists.forEach((list) => {
      if (list.dataset.accordionDisabled === "true") return;

      // Remove click handlers by cloning
      const clone = list.cloneNode(true);
      // Re-add click handlers that just toggle individual items
      clone.querySelectorAll("span[data-faq-question]").forEach((trigger) => {
        trigger.addEventListener("click", (e) => {
          e.stopPropagation();
          const expanded = trigger.getAttribute("aria-expanded") === "true";
          const controlsId = trigger.getAttribute("aria-controls");
          const answerEl = controlsId
            ? document.getElementById(controlsId)
            : null;

          if (expanded) {
            trigger.setAttribute("aria-expanded", "false");
            if (answerEl) answerEl.style.display = "none";
          } else {
            trigger.setAttribute("aria-expanded", "true");
            if (answerEl) {
              answerEl.style.display = "block";
              answerEl.style.height = "auto";
            }
          }
        });
      });

      clone.dataset.accordionDisabled = "true";
      list.parentNode.replaceChild(clone, list);
    });
  }

  function expandAllSections() {
    let expanded = 0;

    // =========================================================
    // 0. Pre-scan: mark elements that should never be expanded
    // =========================================================
    markSkipElements();

    // =========================================================
    // 0b. Expand truncated cells inline (prevent popover/modal)
    // =========================================================
    expandTruncatedCellsInline();
    blockTruncatePopovers();

    // =========================================================
    // 1. Product overview attributes table "表示を増やす" / "See more"
    //    Located in the product overview/facts section (top area)
    // =========================================================
    const overviewExpanders = document.querySelectorAll(
      [
        // Product facts "See more" / "表示を増やす"
        '#productOverview_feature_div .a-expander-header[aria-expanded="false"]',
        '#productOverview_feature_div a-expander-header[aria-expanded="false"]',
        '#poExpander .a-expander-header[aria-expanded="false"]',
        '.po-expander .a-expander-header[aria-expanded="false"]',
        // Product facts desktop section
        '#productFactsDesktop_Section .a-expander-header[aria-expanded="false"]',
        '.product-facts .a-expander-header[aria-expanded="false"]',
      ].join(",")
    );
    overviewExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      if (safeClick(el)) expanded++;
    });

    // =========================================================
    // 2. "この商品について" / "About this item" - "もっと見る" / "See more"
    //    The bullet point list that gets truncated
    // =========================================================
    const featureBulletExpanders = document.querySelectorAll(
      [
        '#featurebullets_feature_div .a-expander-header[aria-expanded="false"]',
        '#feature-bullets .a-expander-header[aria-expanded="false"]',
        '#featurebullets_feature_div a[data-action="a-expander-toggle"]',
      ].join(",")
    );
    featureBulletExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      const container = el.closest(".a-expander-partial-collapse-container");
      if (
        !container ||
        container.classList.contains("a-expander-collapsed") ||
        container.style.maxHeight
      ) {
        if (safeClick(el)) expanded++;
      }
    });

    // Also handle the partial-collapse style "もっと見る" links
    const partialCollapseHeaders = document.querySelectorAll(
      ".a-expander-partial-collapse-header"
    );
    partialCollapseHeaders.forEach((header) => {
      if (shouldSkipElement(header)) return;
      const container = header.closest(
        ".a-expander-partial-collapse-container"
      );
      if (container && container.classList.contains("a-expander-collapsed")) {
        if (safeClick(header)) expanded++;
      }
    });

    // =========================================================
    // 3. Bottom "商品情報" section - 商品詳細, サイズ, 機能と仕様, etc.
    //    These are the tabbed/accordion panels in the lower part of the page
    // =========================================================
    const productInfoExpanders = document.querySelectorAll(
      [
        // Product information section (bottom of page)
        '#productDetails_expanderSectionTriggers .a-expander-header[aria-expanded="false"]',
        '#prodDetails .a-expander-header[aria-expanded="false"]',
        '#detailBullets_feature_div .a-expander-header[aria-expanded="false"]',
        // "商品情報" accordion panels
        '#productDetails_feature_div .a-expander-header[aria-expanded="false"]',
        '#productDetails_db_sections .a-expander-header[aria-expanded="false"]',
        '#productDetails_techSpec_sections .a-expander-header[aria-expanded="false"]',
        // Newer layout with card-style panels
        '.product-details-grid .a-expander-header[aria-expanded="false"]',
        '#productDetails_expanderTrigger_702[aria-expanded="false"]',
      ].join(",")
    );
    productInfoExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      if (safeClick(el)) expanded++;
    });

    // =========================================================
    // 4. Generic: any remaining collapsed expanders in product area
    // =========================================================
    const allCollapsedHeaders = document.querySelectorAll(
      '.a-expander-header[aria-expanded="false"]'
    );
    allCollapsedHeaders.forEach((header) => {
      if (!isInProductArea(header)) return;
      if (shouldSkipElement(header)) return;
      if (safeClick(header)) expanded++;
    });

    // =========================================================
    // 4b. A+ Content Q&A sections (商品の説明 area)
    //     These are collapsible Q&A panels in the brand story / A+ section.
    //     Amazon uses an exclusive accordion (only one open at a time).
    //     We bypass the accordion logic by directly manipulating DOM/styles
    //     to force all panels open simultaneously.
    // =========================================================
    const aplusExpanders = document.querySelectorAll(
      [
        '#aplus_feature_div .a-expander-header[aria-expanded="false"]',
        '#aplus .a-expander-header[aria-expanded="false"]',
        '#aplusProductDescription .a-expander-header[aria-expanded="false"]',
        '#productDescription_feature_div .a-expander-header[aria-expanded="false"]',
        '.aplus-module .a-expander-header[aria-expanded="false"]',
        '[class*="aplus"] .a-expander-header[aria-expanded="false"]',
      ].join(",")
    );
    aplusExpanders.forEach((el) => {
      if (shouldSkipElement(el)) return;
      if (safeClick(el)) expanded++;
    });

    // Force-expand A+ accordion panels by directly manipulating DOM
    forceExpandAplusAccordions();

    // =========================================================
    // 5. Handle "表示を増やす" / "See more" text links specifically
    //    These may be <a> or <span> elements with specific text
    // =========================================================
    const allLinks = document.querySelectorAll(
      "a.a-expander-header, span.a-expander-header, .a-expander-prompt"
    );
    allLinks.forEach((link) => {
      // Skip links inside cells already expanded inline
      const parentCell = link.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      // Skip links that are popover triggers (handled by inline expansion)
      if (
        link.hasAttribute("data-a-popover") ||
        link.getAttribute("data-action") === "a-popover" ||
        link.dataset.popoverBlocked === "true"
      ) return;

      const text = link.textContent.trim();
      const expandTexts = [
        "表示を増やす",
        "表示件数を増やす",
        "さらに表示する",
        "もっと見る",
        "続きを見る",
        "See more",
        "Read more",
        "Show more",
        "Mehr anzeigen",
        "Ver más",
        "Voir plus",
        "Mostra di più",
        "Meer weergeven",
      ];
      // Skip navigation links
      if (shouldSkipElement(link)) return;

      if (expandTexts.some((t) => text.includes(t))) {
        const isAlreadyExpanded =
          link.getAttribute("aria-expanded") === "true" ||
          link
            .closest(".a-expander-container")
            ?.classList.contains("a-expander-expanded");
        if (!isAlreadyExpanded) {
          if (safeClick(link)) expanded++;
        }
      }
    });

    // =========================================================
    // 6. Product attribute table "表示を増やす" (truncated specs)
    //    The tech-spec / product-info table that truncates rows
    //    and shows a "表示を増やす ▼" link at the bottom.
    //    Uses various structures: a-truncate, showMoreLink, etc.
    // =========================================================

    // 6a. Handle a-truncate based "See more" in product tables
    const truncateToggles = document.querySelectorAll(
      [
        ".a-truncate-cut + .a-truncate-see-more",
        ".a-truncate .a-link-normal",
        '#technicalSpecifications_feature_div a[href="javascript:void(0)"]',
        '#productDetails_techSpec_section_1 a[href="javascript:void(0)"]',
      ].join(",")
    );
    truncateToggles.forEach((toggle) => {
      // Skip if already handled by inline expansion
      const parentCell = toggle.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      if (toggle.dataset.popoverBlocked === "true") return;

      const container = toggle.closest(".a-truncate");
      if (container && container.dataset.inlineExpanded === "true") return;
      if (container && container.querySelector(".a-truncate-cut")) {
        if (safeClick(toggle)) expanded++;
      }
    });

    // 6b. Handle "See more" / "表示を増やす" links that expand hidden table rows
    const tableExpanders = document.querySelectorAll(
      [
        "#poShowMoreLink",
        "#showMoreLink",
        ".po-show-more a",
        'a[data-action="po-show-more"]',
        '#productOverview_feature_div a[href="javascript:void(0)"]',
        ".a-size-small.po-truncate-link a",
      ].join(",")
    );
    tableExpanders.forEach((link) => {
      // Skip if already handled by inline expansion
      const parentCell = link.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      if (link.dataset.popoverBlocked === "true") return;
      if (
        link.hasAttribute("data-a-popover") ||
        link.getAttribute("data-action") === "a-popover"
      ) return;

      const text = link.textContent.trim();
      // Skip navigation links
      if (shouldSkipElement(link)) return;

      if (
        text.includes("表示を増やす") ||
        text.includes("表示件数を増やす") ||
        text.includes("さらに表示する") ||
        text.includes("See more") ||
        text.includes("Show more")
      ) {
        if (safeClick(link)) expanded++;
      }
    });

    // 6c. Broad fallback: find any clickable element with expand text
    //     inside product detail/overview tables that hasn't been handled
    const allClickables = document.querySelectorAll(
      [
        '#productOverview_feature_div a',
        '#productOverview_feature_div span[role="button"]',
        '#poExpander a',
        '.po-expander a',
        '#technicalSpecifications_feature_div a',
        '#productDetails_techSpec_section_1 a',
        'table.a-normal a',
        'table.prodDetTable a',
      ].join(",")
    );
    allClickables.forEach((el) => {
      // Skip if already handled by inline expansion
      const parentCell = el.closest("td");
      if (parentCell && parentCell.dataset.inlineExpanded === "true") return;
      if (el.dataset.popoverBlocked === "true") return;
      if (
        el.hasAttribute("data-a-popover") ||
        el.getAttribute("data-action") === "a-popover"
      ) return;

      const text = el.textContent.trim();
      // Skip navigation links
      if (shouldSkipElement(el)) return;

      if (text.includes("表示を増やす") || text.includes("表示件数を増やす") || text.includes("さらに表示する") || text.includes("See more")) {
        if (safeClick(el)) expanded++;
      }
    });

    return expanded;
  }

  /**
   * Check if an element is within the product content area
   * (excludes reviews, Q&A, recommendations, etc.)
   */
  function isInProductArea(el) {
    // Exclude areas we don't want to auto-expand
    const excludeSelectors = [
      "#reviewsMedley",
      "#ask-btf_feature_div",
      "#similarities_feature_div",
      "#sp_detail",
      "#sponsoredProducts",
      "#rhf",
      "#navFooter",
      '[data-feature-name="reviewFeatureGroup"]',
      "#smbLogoImg_feature_div",
      "#smb_feature_div",
      ".smb-container",
      "#olp_feature_div",
      "#moreBuyingChoices_feature_div",
      "#extraProductInfoTips_feature_div",
      "#productAlert_feature_div",
      "#legal_feature_div",
      "#globalStoreBadgePopover_feature_div",
      '[id*="smbLogo"]',
      '[id*="smb_"]',
    ];
    if (excludeSelectors.some((sel) => el.closest(sel))) {
      return false;
    }

    // Include areas we want to expand
    const includeSelectors = [
      "#centerCol",
      "#dp-container",
      "#productDetails_feature_div",
      "#detailBullets_feature_div",
      "#productFactsDesktop_Section",
      "#featurebullets_feature_div",
      "#feature-bullets",
      "#productOverview_feature_div",
      "#productDescription_feature_div",
      "#aplus_feature_div",
      "#prodDetails",
      "#productDetails_db_sections",
      "#productDetails_techSpec_sections",
      ".product-details-grid",
      '[id*="productDetail"]',
    ];
    return includeSelectors.some((sel) => el.closest(sel));
  }

  /**
   * Run expansion with retries to handle lazy-loaded content.
   * Uses increasing delays to catch content that loads later.
   */
  function runWithRetries() {
    // Multiple attempts at increasing intervals to catch lazy-loaded content
    const delays = [500, 1000, 1500, 2000, 3000, 4000, 6000, 9000];
    delays.forEach((delay) => {
      setTimeout(expandAllSections, delay);
    });
  }

  // Inject CSS to ensure inline-expanded text displays properly
  const style = document.createElement("style");
  style.textContent = `
    /* Force full text visible in product overview truncated cells */
    [data-inline-expanded="true"] .a-truncate-full,
    .a-truncate-full.dp-auto-expanded {
      display: inline !important;
      position: static !important;
      clip: auto !important;
      width: auto !important;
      height: auto !important;
      overflow: visible !important;
    }
    [data-inline-expanded="true"] .a-truncate-cut {
      display: none !important;
    }
    [data-inline-expanded="true"] .a-truncate-see-more,
    [data-inline-expanded="true"] a[data-action="a-popover"] {
      display: none !important;
    }
    /* Force A+ FAQ accordion panels to stay open */
    span[data-faq-question][aria-expanded="true"] ~ .aplus-answer,
    span[data-faq-question][aria-expanded="true"] ~ p.aplus-answer,
    li.faq-block .aplus-answer,
    li.faq-block [id^="faq_a_"] {
      display: block !important;
      visibility: visible !important;
      height: auto !important;
      max-height: none !important;
      overflow: visible !important;
      opacity: 1 !important;
    }
  `;
  document.head.appendChild(style);

  // Initial run after page load
  runWithRetries();

  // Observe DOM changes for dynamically loaded content
  const observer = new MutationObserver((mutations) => {
    let shouldExpand = false;
    for (const mutation of mutations) {
      if (mutation.addedNodes.length > 0) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType === 1 && node.querySelector) {
            if (
              node.querySelector('[aria-expanded="false"]') ||
              node.querySelector(".a-expander-collapsed") ||
              node.querySelector(".a-expander-header") ||
              node.matches?.('[aria-expanded="false"]')
            ) {
              shouldExpand = true;
              break;
            }
          }
        }
      }
      if (shouldExpand) break;
    }

    if (shouldExpand) {
      setTimeout(expandAllSections, 500);
    }
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  });
})();