Skip to content

Instantly share code, notes, and snippets.

@curreta
Created January 16, 2026 23:52
Show Gist options
  • Select an option

  • Save curreta/89849c2943c4f02338148c6fca0f52aa to your computer and use it in GitHub Desktop.

Select an option

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
// ==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