Mydealz Navigation

Eingefärbtes Navigationmenü mit Links zu Feedback, Sammlung und Bookmarklets

  1. // ==UserScript==
  2. // @name Mydealz Navigation
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Eingefärbtes Navigationmenü mit Links zu Feedback, Sammlung und Bookmarklets
  6. // @author MD928835
  7. // @license MIT
  8. // @match https://www.mydealz.de/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // --- Konfiguration ---
  16. const customCSS = `
  17. .nav, .subNav--light { background-color: #009900 !important; transition: none !important; display: flex; align-items: center; }
  18. .nav a, .nav span, .nav button, .subNav--light a, .subNav--light span, .subNav--light button { color: white !important; font-weight: bold !important; }
  19. .nav svg, .subNav--light svg { color: white !important; fill: white !important; }
  20. .input-widget .search-icon { color: #009900 !important; fill: #009900 !important; }
  21. .input--search { background-color: white !important; color: #333 !important; }
  22. .nav-button.button--type-secondary, .nav button, .subNav--light button, .button--type-tag, .button--mode-flat { background-color: transparent !important; border-color: transparent !important; }
  23. .nav button:hover, .subNav--light button:hover, .button--type-tag:hover, .button--mode-flat:hover, .nav a:hover, .nav span:hover, .nav button:hover, .subNav--light a:hover, .subNav--light span:hover, .subNav--light button:hover { background-color: transparent !important; }
  24. [data-t="dealAlarms"]:hover, a[href*="deal-alarm"]:hover { background-color: transparent !important; }
  25. .nav-logo > span { display: none !important; }
  26. .nav-logo { background-image: url("https://www.mydealz.de/assets/img/logo/default-light_d4b86.svg") !important; background-repeat: no-repeat !important; background-position: center !important; background-size: contain !important; width: 195px !important; height: 60px !important; pointer-events: auto !important; cursor: pointer; flex-shrink: 0; }
  27. .nav-hideSearch .button--mode-brand { background-color: white !important; color: #005B94 !important; }
  28. .nav-hideSearch .button--mode-brand svg, .nav-hideSearch .button--mode-brand span { color: #005B94 !important; fill: #005B94 !important; }
  29. .threadItemCard-img .scrollBox-container:not(.carousel--isNext) { display: none !important; }
  30.  
  31. /* --- LAYOUT FÜR NAVIGATION --- */
  32. header .subNav--light { height: auto !important; min-height: 40px !important; position: relative; }
  33. header .subNav--light .scrollBox { position: relative; }
  34. /* -------------------------------------------- */
  35. header .subNav--light .scrollBox-container {
  36. display: flex !important;
  37. flex-wrap: nowrap !important;
  38. align-items: stretch !important;
  39. justify-content: flex-start !important;
  40. width: 100%;
  41. overflow-x: auto !important;
  42. padding: 0 5px !important;
  43. margin: 0 !important;
  44. box-sizing: border-box;
  45. background-color: #009900 !important;
  46.  
  47. /* --- Scrollbar verstecken --- */
  48. scrollbar-width: none; /* Firefox */
  49. -ms-overflow-style: none; /* Internet Explorer 10+ */
  50. /* --------------------------------- */
  51. }
  52. /* --- NEU: Scrollbar verstecken (WebKit) --- */
  53. header .subNav--light .scrollBox-container::-webkit-scrollbar {
  54. display: none; /* Safari and Chrome */
  55. width: 0;
  56. height: 0;
  57. }
  58. /* --------------------------------------- */
  59.  
  60. header .subNav--light .scrollBox-item { flex-shrink: 0 !important; margin: 0 !important; padding: 0 !important; display: flex; align-items: center; }
  61. header .subNav--light .scrollBox-item .nav-link { display: flex !important; align-items: center !important; height: 100%; padding: 0 10px !important; white-space: nowrap !important; text-decoration: none !important; color: white !important; font-weight: bold !important; box-sizing: border-box; line-height: 1; cursor: pointer; }
  62. header .subNav--light .scrollBox-item .nav-link svg.icon { width: 18px; height: 18px; margin-right: 6px; flex-shrink: 0; }
  63. header .subNav--light .scrollBox-item:hover { background-color: transparent !important; }
  64. header .subNav--light .scrollBox-item .nav-link:hover { background-color: transparent !important; }
  65. header .subNav--light::before,
  66. header .subNav--light::after,
  67. header .subNav--light .scrollBox::before,
  68. header .subNav--light .scrollBox::after {
  69. display: none !important;
  70. background: none !important;
  71. content: none !important;
  72. width: 0 !important;
  73. z-index: -1 !important;
  74. pointer-events: none !important;
  75. }
  76. /* ----------------------------------------------------------------------- */
  77.  
  78.  
  79. /* --- Dropdown Menü Styles --- */
  80. .mydealz-dropdown-attached, .mdplus-dropdown-attached {
  81. position: fixed; z-index: 99999 !important; background-color: white; border-radius: 4px;
  82. box-shadow: 0 3px 10px rgba(0,0,0,0.2);
  83. padding: 8px 0; margin-top: 2px;
  84. min-width: 220px;
  85. opacity: 0;
  86. visibility: hidden;
  87. transform: translateY(-5px);
  88. transition: opacity 0.15s ease-in-out, transform 0.15s ease-in-out, visibility 0s linear 0.15s;
  89. }
  90. /* Sichtbarer Zustand */
  91. .dropdown-visible {
  92. opacity: 1;
  93. visibility: visible;
  94. transform: translateY(0);
  95. transition: opacity 0.15s ease-in-out, transform 0.15s ease-in-out, visibility 0s linear 0s;
  96. }
  97. .mydealz-dropdown-attached { width: 220px; }
  98. .mdplus-dropdown-attached { width: 240px; }
  99. .mydealz-dropdown-item, .mdplus-dropdown-item { display: block; padding: 10px 16px; color: #333 !important; text-decoration: none; white-space: nowrap; font-weight: normal !important; background-color: transparent; transition: background-color 0.1s ease; /* Hover-Transition */ }
  100. .mydealz-dropdown-item:hover, .mdplus-dropdown-item:hover { background-color: #f0f0f0 !important; color: #333 !important; }
  101. .mdplus-dropdown-item { cursor: pointer; }
  102. .mdplus-divider { height: 1px; margin: 8px 0; background-color: #e5e5e5; }
  103. .mdplus-add-item {
  104. color: #333 !important;
  105. font-weight: bold !important;
  106. }
  107. /* ----------------------------------------------------------------- */
  108.  
  109. /* --- Mobile Ansicht (Nur Icons) --- */
  110. @media (max-width: 767px) {
  111. header .subNav--light .scrollBox-item .nav-link span {
  112. display: none;
  113. }
  114. header .subNav--light .scrollBox-item .nav-link svg.icon {
  115. margin-right: 0;
  116. }
  117. header .subNav--light .scrollBox-item .nav-link {
  118. padding: 0 12px !important;
  119. }
  120. }
  121.  
  122. /* --- Modal Styles --- */
  123. .mdplus-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 10001; }
  124. .mdplus-modal-content { background-color: white; border-radius: 8px; padding: 24px; width: 500px; max-width: 90%; }
  125. .mdplus-modal-title { font-size: 18px; font-weight: bold; margin-bottom: 16px; color: #333; }
  126. .mdplus-form-group { margin-bottom: 16px; }
  127. .mdplus-form-label { display: block; margin-bottom: 8px; font-weight: bold; color: #555;}
  128. .mdplus-form-input, .mdplus-form-textarea { width: 100%; padding: 10px 12px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; font-size: 14px; color: #333; }
  129. .mdplus-form-textarea { min-height: 100px; font-family: monospace; resize: vertical; }
  130. .mdplus-form-error { color: red; font-size: 12px; margin-top: 4px; display: none; }
  131. .mdplus-modal-actions { display: flex; justify-content: flex-end; margin-top: 24px; }
  132. .mdplus-btn { padding: 10px 18px; border-radius: 4px; cursor: pointer; border: none; margin-left: 8px; font-weight: bold; font-size: 14px; transition: background-color 0.2s ease; }
  133. .mdplus-btn-cancel { background-color: #f0f0f0; color: #555; }
  134. .mdplus-btn-cancel:hover { background-color: #e0e0e0; }
  135. .mdplus-btn-save { background-color: #009900; color: white; }
  136. .mdplus-btn-save:hover { background-color: #007700; }
  137. `;
  138.  
  139. // --- Daten ---
  140. const categoryTree = [ { name: "Auto & Motorrad", url: "/gruppe/auto-motorrad" }, { name: "Beauty & Gesundheit", url: "/gruppe/beauty" }, { name: "Dienstleistungen & Verträge", url: "/gruppe/dienstleistungen-vertraege" }, { name: "Elektronik", url: "/gruppe/elektronik" }, { name: "Family & Kids", url: "/gruppe/family-kids" }, { name: "Fashion & Accessoires", url: "/gruppe/fashion-accessoires" }, { name: "Gaming", url: "/gruppe/gaming" }, { name: "Garten & Baumarkt", url: "/gruppe/garten-baumarkt" }, { name: "Home & Living", url: "/gruppe/home-living" }, { name: "Kultur & Freizeit", url: "/gruppe/kultur-freizeit" }, { name: "Lebensmittel & Haushalt", url: "/gruppe/food" }, { name: "Sport & Outdoor", url: "/gruppe/sport" }, { name: "Telefon- & Internet-Verträge", url: "/gruppe/telefon-internet" }, { name: "Urlaub & Reisen", url: "/gruppe/reisen" }, { name: "Versicherung & Finanzen", url: "/gruppe/vertraege-finanzen" } ];
  141. const vouchers = [ { name: "Adidas", url: "/gutscheine/adidas-de" }, { name: "Amazon", url: "/gutscheine/amazon-de" }, { name: "IKEA", url: "/gutscheine/ikea-com" }, { name: "Lidl", url: "/gutscheine/lidl-de" }, { name: "Media Markt", url: "/gutscheine/mediamarkt-de" }, { name: "OTTO", url: "/gutscheine/otto-de" }, { name: "Saturn", url: "/gutscheine/saturn-de" }, { name: "Zalando", url: "/gutscheine/zalando-de" }, { name: "alle anzeigen ", url: "/gutscheine/" } ];
  142.  
  143. // --- MD+ Funktionen ---
  144. function loadBookmarklets() { try { const bm = localStorage.getItem('mdplus_bookmarklets'); return bm ? JSON.parse(bm) : []; } catch (e) { console.error("[NavScript] Error loading bookmarklets:", e); return []; } }
  145. function saveBookmarklets(bms) { try { localStorage.setItem('mdplus_bookmarklets', JSON.stringify(bms)); } catch (e) { console.error("[NavScript] Error saving bookmarklets:", e); } }
  146. function addBookmarklet(title, code) { const bms = loadBookmarklets(); const idx = bms.findIndex(b => b.title === title); if (idx > -1) bms[idx] = {title, code}; else bms.push({title, code}); bms.sort((a, b) => a.title.localeCompare(b.title)); saveBookmarklets(bms); return bms; }
  147. function executeBookmarklet(code) { console.log("[NavScript] Attempting bookmarklet execution via script tag:", code.substring(0,100)+"..."); let finalCode = code; try { let cleanCode = code.trim().startsWith('javascript:') ? code.trim().substring(11) : code.trim(); try { const decoded = decodeURIComponent(cleanCode); cleanCode = decoded; } catch (e) { console.warn("[NavScript] Failed to decode bookmarklet code:", e); } finalCode = cleanCode; if (finalCode.startsWith('(function(){') && finalCode.endsWith('})()')) { finalCode = finalCode.substring('(function(){'.length, finalCode.length - '})()'.length); } if (finalCode.endsWith(';')) { finalCode = finalCode.slice(0, -1); } const script = document.createElement('script'); script.textContent = finalCode; document.head.appendChild(script); document.head.removeChild(script); } catch (error) { console.error('[NavScript] Bookmarklet execution error (script tag setup):', error); alert(`Bookmarklet Error (script tag setup):\n\n${error.name}: ${error.message}`); } }
  148. function createAddBookmarkletModal() { if (document.querySelector('.mdplus-modal')) return; const modal = document.createElement('div'); modal.className = 'mdplus-modal'; modal.innerHTML = `<div class="mdplus-modal-content"><div class="mdplus-modal-title">Bookmarklet hinzufügen</div><div class="mdplus-form-group"><label class="mdplus-form-label" for="bookmarklet-title">Titel</label><input type="text" id="bookmarklet-title" class="mdplus-form-input" placeholder="Name des Bookmarklets"></div><div class="mdplus-form-group"><label class="mdplus-form-label" for="bookmarklet-code">JavaScript-Code</label><textarea id="bookmarklet-code" class="mdplus-form-textarea" placeholder="javascript:... oder reiner JS-Code"></textarea><div id="bookmarklet-code-error" class="mdplus-form-error">Gültiger JavaScript-Code erforderlich.</div></div><div class="mdplus-modal-actions"><button class="mdplus-btn mdplus-btn-cancel">Abbrechen</button><button class="mdplus-btn mdplus-btn-save">Speichern</button></div></div>`; const titleInput = modal.querySelector('#bookmarklet-title'); const codeInput = modal.querySelector('#bookmarklet-code'); const errorElement = modal.querySelector('#bookmarklet-code-error'); modal.addEventListener('click', (e) => { if (e.target === modal) document.body.removeChild(modal); }); modal.querySelector('.mdplus-btn-cancel').addEventListener('click', () => document.body.removeChild(modal)); modal.querySelector('.mdplus-btn-save').addEventListener('click', () => { const title = titleInput.value.trim(); let code = codeInput.value.trim(); errorElement.style.display = 'none'; if (!title) { alert('Titel fehlt.'); titleInput.focus(); return; } if (!code) { alert('Code fehlt.'); codeInput.focus(); return; } if (!code.startsWith('javascript:')) code = 'javascript:' + code; if (code.length < "javascript:;".length && !code.startsWith('javascript:alert')) { errorElement.textContent = "Code zu kurz/ungültig."; errorElement.style.display = 'block'; codeInput.focus(); return; } addBookmarklet(title, code); document.body.removeChild(modal); updateMDPlusDropdown(); }); document.body.appendChild(modal); titleInput.focus(); }
  149. function updateMDPlusDropdown(dropdownElement) { if (!dropdownElement) { dropdownElement = dropdownCache['mdplus']; if (!dropdownElement) return; } const bms = loadBookmarklets(); dropdownElement.innerHTML = ''; if (bms.length === 0) { dropdownElement.innerHTML = '<div style="padding:10px 16px; color: #888; font-style: italic;">Keine Bookmarklets</div>'; } else { bms.forEach(bm => { const item = document.createElement('div'); item.className = 'mdplus-dropdown-item'; item.textContent = bm.title; item.title = bm.code; item.addEventListener('click', (e) => { e.stopPropagation(); executeBookmarklet(bm.code); hideDropdown(dropdownElement); }); dropdownElement.appendChild(item); }); } const divider = document.createElement('div'); divider.className = 'mdplus-divider'; dropdownElement.appendChild(divider); const addItem = document.createElement('div'); addItem.className = 'mdplus-dropdown-item mdplus-add-item'; addItem.textContent = 'Bookmarklet hinzufügen...'; addItem.addEventListener('click', (e) => { e.stopPropagation(); hideDropdown(dropdownElement); createAddBookmarkletModal(); }); dropdownElement.appendChild(addItem); }
  150.  
  151. // --- Hauptlogik ---
  152. let subNavReplaced = false;
  153. let dropdownCache = {};
  154. let currentVisibleDropdown = null;
  155. let hideTimeout = null;
  156.  
  157. // *** createNavItem mit title Attribut ***
  158. function createNavItem(text, url, iconName) {
  159. const item = document.createElement('div');
  160. item.className = 'scrollBox-item';
  161. const link = document.createElement('a');
  162. link.className = 'nav-link';
  163. link.href = url;
  164. link.innerHTML = `<svg width="18" height="18" class="icon icon--${iconName}"><use xlink:href="/assets/img/ico_f3562.svg#${iconName}"></use></svg><span>${text}</span>`;
  165. link.title = text; // Tooltip hinzufügen
  166. item.appendChild(link);
  167. return item;
  168. }
  169. // *** createDropdownItem mit title Attribut für den Hauptlink ***
  170. function createDropdownItem(text, url, iconName, dropdownKey, dropdownCssClass, items) {
  171. const item = document.createElement('div');
  172. item.className = 'scrollBox-item has-dropdown';
  173. item.dataset.dropdownKey = dropdownKey;
  174. const link = document.createElement('a');
  175. link.className = 'nav-link';
  176. link.href = url;
  177. link.innerHTML = `<svg width="18" height="18" class="icon icon--${iconName}"><use xlink:href="/assets/img/ico_f3562.svg#${iconName}"></use></svg><span>${text}</span>`;
  178. link.title = text; // Tooltip hinzufügen
  179. if (url === '#') link.addEventListener('click', e => e.preventDefault());
  180. item.appendChild(link);
  181. const dropdown = document.createElement('div');
  182. dropdown.className = dropdownCssClass + '-attached';
  183. dropdown.dataset.dropdownKey = dropdownKey;
  184. items.forEach(si => {
  185. const l = document.createElement('a');
  186. l.className = dropdownCssClass.includes('mydealz') ? 'mydealz-dropdown-item' : 'mdplus-dropdown-item';
  187. l.href = si.url;
  188. l.textContent = si.name;
  189. dropdown.appendChild(l);
  190. });
  191. dropdownCache[dropdownKey] = dropdown;
  192. return item;
  193. }
  194. // *** createMdPlusDropdownItem mit title Attribut ***
  195. function createMdPlusDropdownItem() {
  196. const dropdownKey = 'mdplus';
  197. const item = document.createElement('div');
  198. item.className = 'scrollBox-item has-dropdown nav-item-mdplus';
  199. item.dataset.dropdownKey = dropdownKey;
  200. const link = document.createElement('a');
  201. link.className = 'nav-link';
  202. link.href = "#";
  203. const iconName = 'bookmark';
  204. link.innerHTML = `<svg width="18" height="18" class="icon icon--${iconName}"><use xlink:href="/assets/img/ico_f3562.svg#${iconName}"></use></svg><span>MD+</span>`;
  205. link.title = "MD+"; // Tooltip hinzufügen
  206. link.addEventListener('click', (e) => e.preventDefault());
  207. item.appendChild(link);
  208. const dropdown = document.createElement('div');
  209. dropdown.className = 'mdplus-dropdown-attached';
  210. dropdown.dataset.dropdownKey = dropdownKey;
  211. dropdown.innerHTML = '<span style="padding:10px 16px; display: block; color: #888;">Lade...</span>';
  212. dropdownCache[dropdownKey] = dropdown;
  213. return item;
  214. }
  215.  
  216. function showDropdown(triggerItem) {
  217. clearTimeout(hideTimeout);
  218. const key = triggerItem.dataset.dropdownKey;
  219. const dropdownElement = dropdownCache[key];
  220. if (!dropdownElement) { console.error(`[NavScript] Dropdown element not found for key: ${key}`); return; }
  221. if (currentVisibleDropdown && currentVisibleDropdown !== dropdownElement) { hideDropdown(currentVisibleDropdown); }
  222. const rect = triggerItem.getBoundingClientRect();
  223. dropdownElement.style.top = `${rect.bottom}px`;
  224. dropdownElement.style.left = `${rect.left}px`;
  225. const dropdownRight = rect.left + dropdownElement.offsetWidth;
  226. if (dropdownRight > window.innerWidth) {
  227. dropdownElement.style.left = `${window.innerWidth - dropdownElement.offsetWidth - 10}px`; // 10px Puffer
  228. }
  229.  
  230. if (key === 'mdplus') { updateMDPlusDropdown(dropdownElement); }
  231. if (!dropdownElement.parentNode) { document.body.appendChild(dropdownElement); }
  232. requestAnimationFrame(() => {
  233. dropdownElement.classList.add('dropdown-visible');
  234. });
  235. currentVisibleDropdown = dropdownElement;
  236. if (!dropdownElement.hasAttribute('data-listeners-attached')) {
  237. dropdownElement.addEventListener('mouseenter', () => { clearTimeout(hideTimeout); });
  238. dropdownElement.addEventListener('mouseleave', () => { startHideTimeout(dropdownElement); });
  239. dropdownElement.setAttribute('data-listeners-attached', 'true');
  240. }
  241. }
  242. function hideDropdown(dropdownElement) {
  243. if (dropdownElement) {
  244. dropdownElement.classList.remove('dropdown-visible');
  245. if (dropdownElement.parentNode === document.body) {
  246. setTimeout(() => {
  247. // Nur entfernen, wenn es immer noch versteckt ist und im body hängt
  248. if (!dropdownElement.classList.contains('dropdown-visible') && dropdownElement.parentNode === document.body) {
  249. const currentCachedElement = dropdownCache[dropdownElement.dataset.dropdownKey];
  250. if (currentCachedElement === dropdownElement) {
  251. document.body.removeChild(dropdownElement);
  252. }
  253. }
  254. }, 200); // Zeit muss länger sein als die CSS-Transition-Dauer
  255. }
  256. if (currentVisibleDropdown === dropdownElement) { currentVisibleDropdown = null; }
  257. }
  258. }
  259. function startHideTimeout(elementToHide = currentVisibleDropdown) {
  260. clearTimeout(hideTimeout);
  261. hideTimeout = setTimeout(() => {
  262. hideDropdown(elementToHide);
  263. }, 300); // Verzögerung bevor das Dropdown verschwindet
  264. }
  265.  
  266. function replaceSubNav() {
  267. if (subNavReplaced) return;
  268. // Suche nach dem potentiellen .scrollBox Wrapper UND dem .scrollBox-container darin
  269. const scrollBoxWrapper = document.querySelector('header .subNav--light .scrollBox');
  270. const subNavContainer = document.querySelector('header .subNav--light .scrollBox-container');
  271. const targetContainer = scrollBoxWrapper ? scrollBoxWrapper : subNavContainer?.parentNode; // Nimm den Wrapper, wenn er existiert, sonst den direkten Parent
  272.  
  273. if (!targetContainer || targetContainer.querySelector('.scrollBox-container[data-nav-modified="true"]')) {
  274. //console.log("[NavScript] Target container not found or already modified.");
  275. return;
  276. }
  277.  
  278. const originalScrollBoxContainer = targetContainer.querySelector('.scrollBox-container');
  279. if (!originalScrollBoxContainer) {
  280. console.log("[NavScript] Original scrollBox-container not found inside target.");
  281. return;
  282. }
  283.  
  284.  
  285. console.log("[NavScript] Found target container, replacing content...");
  286. dropdownCache = {}; // Reset cache bei Neuaufbau
  287. const newNav = document.createElement('div');
  288. newNav.className = 'scrollBox-container'; // Behält die Originalklasse für eventuelle Basisstyles bei
  289. newNav.dataset.navModified = 'true'; // Markierung, dass das Skript aktiv war
  290.  
  291. // Menüpunkte
  292. newNav.appendChild(createDropdownItem("Kategorien", "/gruppe/", "categories", "kategorien", "mydealz-dropdown", categoryTree));
  293. newNav.appendChild(createDropdownItem("Gutscheine", "/gutscheine/", "voucher", "gutscheine", "mydealz-dropdown", vouchers));
  294. newNav.appendChild(createNavItem("Deals", "/deals-new", "tag"));
  295. newNav.appendChild(createNavItem("Freebies", "/gruppe/freebies?temperatureFrom=any", "gift"));
  296. newNav.appendChild(createNavItem("Diskussionen", "/diskussion", "comments"));
  297. newNav.appendChild(createNavItem("Feedback", "/feedback", "campaign"));
  298. newNav.appendChild(createNavItem("Sammlung", "/2035404#comments", "cake"));
  299. newNav.appendChild(createMdPlusDropdownItem());
  300.  
  301. try {
  302. targetContainer.replaceChild(newNav, originalScrollBoxContainer);
  303. subNavReplaced = true; // Flag setzen
  304. console.log("[NavScript] Container content replaced. Attaching listeners...");
  305.  
  306. newNav.querySelectorAll('.has-dropdown').forEach(item => {
  307. const dropdown = dropdownCache[item.dataset.dropdownKey];
  308. if (!dropdown) {
  309. console.warn(`[NavScript] Dropdown element for key ${item.dataset.dropdownKey} not in cache after creation.`);
  310. return;
  311. }
  312. // Mouseenter auf das Menüitem zeigt das Dropdown
  313. item.addEventListener('mouseenter', () => {
  314. showDropdown(item);
  315. });
  316. item.addEventListener('mouseleave', () => {
  317. startHideTimeout();
  318. });
  319. });
  320. console.log("[NavScript] Item listeners attached.");
  321.  
  322. } catch (error) {
  323. console.error("[NavScript] Error during replace/listeners:", error);
  324. subNavReplaced = false; // Reset flag on error
  325. }
  326. }
  327.  
  328. // --- Initialisierung und Beobachtung ---
  329. function initializeNavigation() {
  330. replaceSubNav();
  331. }
  332.  
  333. // CSS nur einmal einfügen
  334. const styleElement = document.createElement('style');
  335. styleElement.id = 'mydealz-nav-custom-styles';
  336. if (!document.getElementById(styleElement.id)) {
  337. styleElement.textContent = customCSS;
  338. document.head.appendChild(styleElement);
  339. console.log("[NavScript] CSS injected.");
  340. }
  341.  
  342. let observer = null;
  343. const parentSelector = 'header .subNav--light';
  344.  
  345. function startObserver() {
  346. const parentNode = document.querySelector(parentSelector);
  347. if (parentNode && !observer) {
  348. observer = new MutationObserver((mutationsList, obs) => {
  349. const modifiedContainer = parentNode.querySelector('.scrollBox-container[data-nav-modified="true"]');
  350. if (!modifiedContainer) {
  351. //console.log("[NavScript Observer] Modified container disappeared. Resetting flag and attempting re-init...");
  352. subNavReplaced = false;
  353. initializeNavigation();
  354. } else {
  355. Object.values(dropdownCache).forEach(dropdown => {
  356. if (dropdown.classList.contains('dropdown-visible') && !dropdown.parentNode) {
  357. console.warn("[NavScript Observer] Visible dropdown detached from body. Re-attaching.");
  358. // Finde das zugehörige Trigger-Item neu, um Position zu bestimmen
  359. const trigger = parentNode.querySelector(`.has-dropdown[data-dropdown-key="${dropdown.dataset.dropdownKey}"]`);
  360. if (trigger) {
  361. const rect = trigger.getBoundingClientRect();
  362. dropdown.style.top = `${rect.bottom}px`;
  363. dropdown.style.left = `${rect.left}px`;
  364. document.body.appendChild(dropdown);
  365. } else {
  366. hideDropdown(dropdown);
  367. }
  368. }
  369. });
  370. }
  371. });
  372. observer.observe(parentNode, { childList: true, subtree: true });
  373. //console.log("[NavScript] MutationObserver started on:", parentSelector);
  374. } else if (!parentNode) {
  375. // Falls der Elterncontainer noch nicht da ist, später erneut versuchen
  376. //console.log("[NavScript] Parent node for observer not found, retrying...");
  377. setTimeout(startObserver, 500);
  378. }
  379. }
  380.  
  381. // Startlogik
  382. if (document.readyState === 'loading') {
  383. document.addEventListener('DOMContentLoaded', () => {
  384. initializeNavigation();
  385. startObserver();
  386. });
  387. } else {
  388. initializeNavigation();
  389. startObserver();
  390. }
  391.  
  392. setTimeout(initializeNavigation, 750);
  393. // Evtl. noch späterer Versuch für hartnäckige Fälle
  394. setTimeout(initializeNavigation, 2000);
  395.  
  396.  
  397. // Listener um Dropdowns zu schließen, wenn außerhalb geklickt wird
  398. document.addEventListener('click', function(event) {
  399. if (currentVisibleDropdown && !currentVisibleDropdown.contains(event.target)) {
  400. let clickedOnTrigger = false;
  401. document.querySelectorAll('.has-dropdown').forEach(trigger => {
  402. if (trigger.contains(event.target)) {
  403. clickedOnTrigger = true;
  404. }
  405. });
  406. if (!clickedOnTrigger) {
  407. hideDropdown(currentVisibleDropdown);
  408. }
  409. }
  410. }, true); // Use capture phase
  411.  
  412.  
  413. })();