<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>MathJax Custom Select</title>

  <!-- MathJax v3 -->
  <script>
    window.MathJax = {
      tex: { inlineMath: [['\\(','\\)'], ['$', '$']] },
      svg: { fontCache: 'global' }
    };
  </script>
  <script defer src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>

  <style>
    body { font-family: system-ui, sans-serif; padding: 24px; }
    .mj-select { position: relative; width: 360px; }
    .mj-btn {
      width: 100%;
      text-align: left;
      padding: 10px 40px 10px 12px;
      border-radius: 12px;
      border: 1px solid rgba(0,0,0,.18);
      background: #fff;
      box-shadow: 0 1px 2px rgba(0,0,0,.06);
      cursor: pointer;
    }
    .mj-btn:focus { outline: none; box-shadow: 0 0 0 4px rgba(0,120,255,.18); }

    .mj-arrow {
      position: absolute;
      right: 12px;
      top: 50%;
      transform: translateY(-50%);
      pointer-events: none;
      opacity: .65;
    }

    .mj-listbox {
      position: absolute;
      z-index: 10;
      width: 100%;
      margin-top: 8px;
      padding: 6px;
      border-radius: 12px;
      border: 1px solid rgba(0,0,0,.18);
      background: #fff;
      box-shadow: 0 10px 25px rgba(0,0,0,.12);
      display: none;
      max-height: 240px;
      overflow: auto;
    }
    .mj-select[aria-expanded="true"] .mj-listbox { display: block; }

    .mj-option {
      padding: 10px 10px;
      border-radius: 10px;
      cursor: pointer;
    }
    .mj-option:hover { background: rgba(0,0,0,.05); }
    .mj-option[aria-selected="true"] { background: rgba(0,120,255,.12); }
  </style>
</head>

<body>
  <div class="mj-select" id="mjSelect" role="combobox" aria-haspopup="listbox" aria-expanded="false">
    <button class="mj-btn" id="mjBtn" type="button" aria-controls="mjListbox">
      <span id="mjValue">Pick an expression…</span>
    </button>
    <span class="mj-arrow" aria-hidden="true">▾</span>

    <div class="mj-listbox" id="mjListbox" role="listbox" tabindex="-1">
      <div class="mj-option" role="option" tabindex="0" data-value="f1">
        $ \dfrac{a}{b} $
      </div>
      <div class="mj-option" role="option" tabindex="0" data-value="f1">
        x^2
      </div>
      <div class="mj-option" role="option" tabindex="-1" data-value="f2">
        \( \sqrt{x^2 + 1} \)
      </div>
      <div class="mj-option" role="option" tabindex="-1" data-value="f3">
        \( \int_0^1 x^2\,dx = \frac{1}{3} \)
      </div>
    </div>

    <!-- Hidden input if you want to submit a value in a form -->
    <input type="hidden" name="expr" id="exprHidden" value="" />
  </div>

  <script>
    const root = document.getElementById("mjSelect");
    const btn = document.getElementById("mjBtn");
    const valueSpan = document.getElementById("mjValue");
    const listbox = document.getElementById("mjListbox");
    const options = Array.from(listbox.querySelectorAll(".mj-option"));
    const hidden = document.getElementById("exprHidden");

    function open() {
      root.setAttribute("aria-expanded", "true");
      listbox.focus();
    }
    function close() {
      root.setAttribute("aria-expanded", "false");
      btn.focus();
    }
    function toggle() {
      root.getAttribute("aria-expanded") === "true" ? close() : open();
    }

    function selectOption(opt) {
      options.forEach(o => o.setAttribute("aria-selected", "false"));
      opt.setAttribute("aria-selected", "true");
      hidden.value = opt.dataset.value;

      // Put the option's math into the button display
      valueSpan.innerHTML = opt.innerHTML;

      // Re-typeset only the button content (fast)
      if (window.MathJax?.typesetPromise) {
        MathJax.typesetPromise([valueSpan]);
      }
      close();
    }

    btn.addEventListener("click", toggle);

    options.forEach(opt => {
      opt.addEventListener("click", () => selectOption(opt));
      opt.addEventListener("keydown", (e) => {
        if (e.key === "Enter" || e.key === " ") { e.preventDefault(); selectOption(opt); }
      });
    });

    // Keyboard navigation when listbox focused
    listbox.addEventListener("keydown", (e) => {
      const current = document.activeElement.classList.contains("mj-option")
        ? document.activeElement
        : options[0];

      let idx = options.indexOf(current);

      if (e.key === "Escape") { e.preventDefault(); close(); }
      if (e.key === "ArrowDown") { e.preventDefault(); options[Math.min(idx + 1, options.length - 1)].focus(); }
      if (e.key === "ArrowUp") { e.preventDefault(); options[Math.max(idx - 1, 0)].focus(); }
    });

    // Close when clicking outside
    document.addEventListener("click", (e) => {
      if (!root.contains(e.target)) root.setAttribute("aria-expanded", "false");
    });

    // Initial MathJax render for the options list
    window.addEventListener("load", () => {
      if (window.MathJax?.typesetPromise) {
        MathJax.typesetPromise([listbox]);
      }
    });
  </script>
</body>
</html>
