Instantly share code, notes, and snippets.
Created
January 16, 2026 23:52
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save curreta/89849c2943c4f02338148c6fca0f52aa to your computer and use it in GitHub Desktop.
Converts Poshmark's hover-triggered dropdown menus to click-triggered with toggle buttons
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Poshmark Click Menu (No Hover) | |
| // @namespace https://github.com/GarbsNET | |
| // @version 1.5 | |
| // @description Convert Poshmark's hover-triggered dropdown menus to click-triggered with toggle buttons | |
| // @match https://poshmark.com/* | |
| // @match https://www.poshmark.com/* | |
| // @grant none | |
| // @run-at document-start | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| // Block mouseover and click events on nav links IMMEDIATELY at capture phase | |
| // This must run before Vue attaches its handlers | |
| document.addEventListener('mouseover', (e) => { | |
| if (e.target.closest('.header--scrollable__nav__links')) { | |
| e.stopImmediatePropagation(); | |
| } | |
| }, true); | |
| // Block Vue's click handler on nav links, but allow normal navigation | |
| document.addEventListener('click', (e) => { | |
| const navLink = e.target.closest('.header--scrollable__nav__links a'); | |
| const isToggle = e.target.closest('.pm-dropdown-toggle'); | |
| if (navLink && !isToggle) { | |
| // Stop Vue from handling this click (which shows dropdown) | |
| e.stopImmediatePropagation(); | |
| // Don't prevent default - let the browser navigate normally | |
| } | |
| }, true); | |
| // Track which dropdown is open | |
| let activeIndex = -1; | |
| function init() { | |
| const navLinks = document.querySelector('.header--scrollable__nav__links'); | |
| const dropdownContainer = document.querySelector('.header--scrollable__dropdown'); | |
| // Wait for Vue to render the nav elements | |
| if (!navLinks || !dropdownContainer) { | |
| console.log('[Poshmark Click Menu] Waiting for nav elements...'); | |
| setTimeout(init, 500); | |
| return; | |
| } | |
| const navItems = Array.from(navLinks.querySelectorAll('li')); | |
| const dropdowns = Array.from(dropdownContainer.querySelectorAll('.header--scrollable__dropdown__menu')); | |
| // Also wait until there are actual items | |
| if (navItems.length === 0 || dropdowns.length === 0) { | |
| console.log('[Poshmark Click Menu] Waiting for nav items to populate...'); | |
| setTimeout(init, 500); | |
| return; | |
| } | |
| // Prevent running twice | |
| if (document.querySelector('.pm-dropdown-toggle')) { | |
| console.log('[Poshmark Click Menu] Already initialized, skipping'); | |
| return; | |
| } | |
| // Inject styles | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| /* Fix li overflow so toggles are visible and display inline */ | |
| .header--scrollable__nav__links li { | |
| overflow: visible !important; | |
| display: inline-flex !important; | |
| align-items: center !important; | |
| } | |
| /* Toggle button styling */ | |
| .pm-dropdown-toggle { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 18px; | |
| height: 18px; | |
| margin-left: 10px; | |
| border: 1px solid rgba(0, 0, 0, 0.25); | |
| border-radius: 3px; | |
| background: rgba(0, 0, 0, 0.08); | |
| color: #333; | |
| font-size: 9px; | |
| cursor: pointer; | |
| vertical-align: middle; | |
| transition: background 0.15s, border-color 0.15s, transform 0.15s; | |
| user-select: none; | |
| } | |
| .pm-dropdown-toggle:hover { | |
| background: rgba(0, 0, 0, 0.15); | |
| border-color: rgba(0, 0, 0, 0.4); | |
| } | |
| .pm-dropdown-toggle.active { | |
| background: #7f0353; | |
| border-color: #7f0353; | |
| color: #fff; | |
| transform: rotate(180deg); | |
| } | |
| /* Force dropdown visible when we set it */ | |
| .header--scrollable__dropdown__menu.pm-force-show { | |
| display: block !important; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| function closeAllDropdowns() { | |
| dropdowns.forEach(d => { | |
| d.classList.remove('pm-force-show'); | |
| }); | |
| document.querySelectorAll('.pm-dropdown-toggle.active').forEach(btn => { | |
| btn.classList.remove('active'); | |
| }); | |
| activeIndex = -1; | |
| } | |
| function openDropdown(index, toggleBtn) { | |
| closeAllDropdowns(); | |
| if (dropdowns[index]) { | |
| // Use our own class with !important to override Vue's hide class | |
| dropdowns[index].classList.add('pm-force-show'); | |
| if (toggleBtn) toggleBtn.classList.add('active'); | |
| activeIndex = index; | |
| } | |
| } | |
| // Add toggle buttons to nav items | |
| function addToggleButtons() { | |
| const currentNavItems = Array.from(navLinks.querySelectorAll('li')); | |
| const currentDropdowns = Array.from(dropdownContainer.querySelectorAll('.header--scrollable__dropdown__menu')); | |
| currentNavItems.forEach((item, index) => { | |
| // Skip if already has a toggle button | |
| if (item.querySelector('.pm-dropdown-toggle')) return; | |
| // Skip if no corresponding dropdown or dropdown is empty | |
| if (index >= currentDropdowns.length) return; | |
| const dropdown = currentDropdowns[index]; | |
| const hasContent = dropdown.children.length > 0; | |
| if (!hasContent) return; | |
| const toggleBtn = document.createElement('span'); | |
| toggleBtn.className = 'pm-dropdown-toggle'; | |
| toggleBtn.innerHTML = '▼'; | |
| toggleBtn.title = 'Toggle dropdown menu'; | |
| toggleBtn.addEventListener('click', (e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| if (activeIndex === index) { | |
| closeAllDropdowns(); | |
| } else { | |
| openDropdown(index, toggleBtn); | |
| } | |
| }); | |
| // Insert toggle as sibling after the anchor tag | |
| const anchor = item.querySelector('a'); | |
| if (anchor) { | |
| anchor.after(toggleBtn); | |
| } else { | |
| item.appendChild(toggleBtn); | |
| } | |
| }); | |
| const count = document.querySelectorAll('.pm-dropdown-toggle').length; | |
| if (count > 0) { | |
| console.log('[Poshmark Click Menu] Added', count, 'toggle buttons'); | |
| } | |
| } | |
| // Initial add | |
| addToggleButtons(); | |
| // Watch for Vue re-renders that might remove our buttons | |
| const navObserver = new MutationObserver(() => { | |
| addToggleButtons(); | |
| }); | |
| navObserver.observe(navLinks, { childList: true, subtree: true }); | |
| // Close when clicking outside | |
| document.addEventListener('click', (e) => { | |
| const clickedToggle = e.target.closest('.pm-dropdown-toggle'); | |
| const clickedDropdown = e.target.closest('.header--scrollable__dropdown'); | |
| if (!clickedToggle && !clickedDropdown) { | |
| closeAllDropdowns(); | |
| } | |
| }); | |
| // Close on Escape key | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape') { | |
| closeAllDropdowns(); | |
| } | |
| }); | |
| console.log('[Poshmark Click Menu] v1.5 Initialized'); | |
| } | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', init); | |
| } else { | |
| init(); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment