1// Diese Funktion parsed den Inhalt des angegebenen HTML Elements und bildet daraus ein Objekt mit den zugehörigen Daten 2export function getData(rootElement) { 3 function getElement(element) { 4 const object = { 5 }; 6 7 object.title = element.dataset.title; 8 object.uid = element.dataset.uid; 9 object.href = element.dataset.href; 10 object.children = []; 11 12 13 14 if (element.dataset.text) { 15 object.text = element.dataset.text; 16 } 17 18 if (element.dataset.imageS) { 19 object.imageS = element.dataset.imageS; 20 } 21 22 if (element.dataset.imageL) { 23 object.imageL = element.dataset.imageL; 24 } 25 26 if (element.children.length > 0) { 27 Array.from(element.children).forEach(child => { 28 object.children.push(getElement(child.querySelector(':not(li)'))); 29 }); 30 object.expanded = false; 31 } else { 32 object.active = false; 33 } 34 35 return object; 36 } 37 38 const object = getElement(rootElement.querySelector('ul')); 39 const objectData = [object]; 40 41 return objectData; 42} 43 44// Die geparsten Daten werden ergänzt 45export function buildMenuData(data, currentId, parent = null, activeElement = { 46}) { 47 let result = activeElement; 48 49 data.forEach(itemData => { 50 if (itemData.uid === currentId) { 51 itemData.active = true; 52 result = itemData; 53 } else { 54 itemData.active = false; 55 } 56 57 if (parent) { 58 itemData.parent = parent; 59 itemData.depth = parent.depth + 1; 60 } else { 61 itemData.depth = 0; 62 } 63 64 if (itemData.depth === 1 && itemData.title.split(' ').length > 1) { 65 const title = itemData.title; 66 67 itemData.title = title.substring(0, title.lastIndexOf(' ')); 68 itemData.subtitle = title.substring(title.lastIndexOf(' ') + 1, title.length); 69 } 70 71 if (!itemData.parentOfActive) { 72 itemData.parentOfActive = false; 73 } 74 75 itemData.expanded = false; 76 itemData.visible = false; 77 itemData.node; 78 79 itemData.setVisible = function (state) { 80 this.visible = state; 81 82 if (this.node) { 83 this.node.dataset.visible = state; 84 } 85 }; 86 87 itemData.setExpanded = function (state) { 88 this.expanded = state; 89 90 if (this.node && this.node.tagName === 'BUTTON') { 91 this.node.setAttribute('aria-pressed', state); 92 93 if (this.node.nextElementSibling) { 94 this.node.nextElementSibling.setAttribute('aria-expanded', state); 95 96 if (this.depth >= 1 && !this.node.nextElementSibling.classList.contains('hw_menu__page')) { 97 this.node.nextElementSibling.style.maxHeight = this.node.nextElementSibling.scrollHeight + 'px'; 98 99 if (state) { 100 this.node.style.pointerEvents = 'none'; 101 this.node.nextElementSibling.addEventListener('transitionend', function transListener(event) { 102 event.target.style.maxHeight = ''; 103 104 if (event.target.previousElementSibling) { 105 event.target.previousElementSibling.style.pointerEvents = ''; 106 } 107 108 event.target.removeEventListener('transitionend', transListener, false); 109 }); 110 } else { 111 this.node.style.pointerEvents = 'none'; 112 setTimeout(() => { 113 this.node.nextElementSibling.style.maxHeight = '0px'; 114 }, 1); 115 116 this.node.nextElementSibling.addEventListener('transitionend', function transListener(event) { 117 if (event.target.previousElementSibling) { 118 event.target.previousElementSibling.style.pointerEvents = ''; 119 } 120 121 event.target.removeEventListener('transitionend', transListener, false); 122 }); 123 } 124 } 125 } 126 } 127 }; 128 129 itemData.setChildrenVisible = function (state) { 130 getChildren(this).forEach(child => { 131 if (state && child.parent.expanded && child.node) { 132 child.setVisible(true); 133 } else if (child.node) { 134 child.setVisible(false); 135 } 136 }); 137 }; 138 139 if (itemData.active) { 140 getParents(itemData).forEach(parent => { 141 if (parent.parent) { 142 parent.parentOfActive = true; 143 } 144 }); 145 } 146 147 if (itemData.children.length > 0) { 148 const childResult = buildMenuData(itemData.children, currentId, itemData, result); 149 150 if (childResult) { 151 result = childResult; 152 } 153 } 154 }); 155 156 return result; 157} 158 159export function buildMenuList(data, maxDepth, activeElement, classPrefix = 'hw_menu') { 160 const menuList = ` 161 <button type="button" class="${classPrefix}__button" data-depth="${data.depth}" aria-pressed="${data.expanded}" data-visible="${data.visible}" data-parent-of-active="${data.parentOfActive}"> 162 <span class="${classPrefix}__button__inner_text"> 163 <span>${data.title}</span> 164 ${data.subtitle ? `<span>${data.subtitle}</span>` : ''} 165 </span> 166 <span class="${classPrefix}__expand_icon" aria-hidden="true"></span> 167 </button> 168 <ul class="${classPrefix}__list" data-depth="${data.depth}" aria-expanded="${data.expanded}" style="max-height: 0;"> 169 </ul> 170 `; 171 172 173 174 const htmlFragment = document.createRange().createContextualFragment(menuList); 175 const button = htmlFragment.querySelector(`.${classPrefix}__button`); 176 177 178 data.node = button; 179 180 button.addEventListener('click', event => { 181 event.stopImmediatePropagation(); 182 183 const state = !data.expanded; 184 185 data.setExpanded(state); 186 data.setChildrenVisible(state); 187 188 const siblings = data.parent.children.filter(child => child !== data); 189
190 siblings.forEach(sibling => { 191 if (sibling.children.length > 0) { 192 sibling.setExpanded(false); 193 sibling.setChildrenVisible(false); 194 195 sibling.node.style.pointerEvents = ''; 196 } 197 }); 198 }); 199 200 data.children.map(itemData => { 201 if (itemData.depth <= maxDepth) { 202 htmlFragment.querySelector(`.${classPrefix}__list`).append(buildMenuListElement(itemData, true, maxDepth, activeElement, classPrefix)); 203 } else { 204 htmlFragment.querySelector(`.${classPrefix}__list`).append(buildMenuListElement(itemData, false, maxDepth, activeElement, classPrefix)); 205 } 206 }); 207 208 return htmlFragment; 209} 210 211export function buildMenuLink(data, maxDepth, activeElement, classPrefix = 'hw_menu') { 212 let menuLink; 213 214 const activeParent = getParentAtDepth(activeElement, maxDepth); 215 216 if (maxDepth !== 99 && data === activeParent) { 217 data.active = true; 218 } 219 220 if (!data.active) { 221 menuLink = ` 222 <a class="${classPrefix}__link" href="${data.href}" data-depth="${data.depth}" data-active="${data.active}" data-visible="${data.visible}"> 223 <span class="${classPrefix}__link__inner_text"> 224 <span>${data.title}</span> 225 ${data.subtitle ? `<span>${data.subtitle}</span>` : ''} 226 </span> 227 </a> 228 `; 229 } else { 230 menuLink = ` 231 <span class="${classPrefix}__link" data-depth="${data.depth}" data-active="${data.active}" data-visible="${data.visible}"> 232 <span>${data.title}</span> 233 ${data.subtitle ? `<span>${data.subtitle}</span>` : ''} 234 </span> 235 `; 236 } 237 238 const htmlFragment = document.createRange().createContextualFragment(menuLink); 239 const node = htmlFragment.querySelector(`.${classPrefix}__link`); 240 241 data.node = node; 242 243 return htmlFragment; 244} 245 246export function buildMenuListElement(data, recursive, maxDepth, activeElement, classPrefix = 'hw_menu') { 247 248 // Den letzten Elementen wird eine Klasse gegeben, um das padding zu steuern 249 if (data.depth === 1 && data.children.length > 0) { 250 const maxColumns = 3; 251 252 for (let i = 1; i <= maxColumns; i++) { 253 let numItemsInLastRow = data.children.length % i || i; 254 255 for (let j = data.children.length - 1; j >= data.children.length - numItemsInLastRow; j--) { 256 257 if (data.children[j].class) { 258 data.children[j].class = `${data.children[j].class} ${classPrefix}__list_element__last_row-${i}`; 259 260 } else { 261 data.children[j].class = `${classPrefix}__list_element__last_row-${i}`; 262 } 263 } 264 } 265 266 // const numItemsInLastRowXxl = data.children.length % 3 || 3; 267 268 // for (let i = data.children.length - 1; i >= data.children.length - numItemsInLastRowXxl; i--) { 269 // console.log(data.children[i]); 270 271 // if (data.children[i]) { 272 273 // data.children[i].class = `${classPrefix}__list_element__last_row-xxl`; 274 // } 275 // } 276 277 // data.children[data.children.length - 1].class = `${classPrefix}__list_element-1`; 278 // data.children[data.children.length - 2].class = `${classPrefix}__list_element-2`; 279 // data.children[data.children.length - 3].class = `${classPrefix}__list_element-3`; 280 } 281 282 const menuListElement = ` 283 <li class="${classPrefix}__list_element${data.class ? ` ${data.class}` : ''}"> 284 </li> 285 `; 286 287 const htmlFragment = document.createRange().createContextualFragment(menuListElement); 288 289 290 291 if (recursive && data.children.length > 0 && data.depth < maxDepth) { 292 htmlFragment.querySelector(`.${classPrefix}__list_element`).append(buildMenuList(data, maxDepth, activeElement, classPrefix)); 293 } else { 294 htmlFragment.querySelector(`.${classPrefix}__list_element`).append(buildMenuLink(data, maxDepth, activeElement, classPrefix)); 295 } 296 297 return htmlFragment; 298} 299 300export function getElementsAtDepth(data, depth, elementsAtDepth = []) { 301 data.forEach(itemData => { 302 if (itemData.depth === depth) { 303 elementsAtDepth.push(itemData); 304 } else { 305 getElementsAtDepth(itemData.children, depth, elementsAtDepth); 306 } 307 }); 308 309 return elementsAtDepth; 310} 311 312function getChildren(data, children = []) { 313 if (!data.children) { 314 return children; 315 } else { 316 data.children.forEach(child => { 317 children.push(child); 318 319 getChildren(child, children); 320 }); 321 } 322 323 return children; 324} 325 326export function checkForExpanded(data) { 327 if (data.children) { 328 if (data.expanded && !data.parentOfActive) { 329 data.expanded = false;
330 } 331 332 data.children.forEach(child => { 333 if (child.expanded && !child.parentOfActive) { 334 child.expanded = false; 335 } 336 337 checkForExpanded(child); 338 }); 339 } 340} 341 342export function getParentAtDepth(element, depth) { 343 let parentAtDepth = element.parent; 344 345 for (let i = element.depth - 1; i > depth; i--) { 346 parentAtDepth = parentAtDepth.parent; 347 } 348 349 return parentAtDepth; 350} 351 352export function getElementWithUrl(data, url, objects = []) { 353 354 data.forEach(itemData => { 355 console.log(itemData); 356 357 if (itemData.href.indexOf(url) === 0) { 358 objects.push(itemData); 359 } else { 360 getElementWithUrl(itemData.children, url, objects); 361 } 362 }); 363 364 return objects[0]; 365} 366 367export function getParents(data, parents = []) { 368 if (!data.parent) { 369 return parents; 370 } else { 371 parents.push(data.parent); 372 373 getParents(data.parent, parents); 374 } 375 376 return parents; 377} 378 379export function initializeMenu(data, menuType, depth = 0) { 380 const activeElement = data[0].activeElement; 381 const parents = getParents(activeElement); 382 383 parents.forEach(parent => { 384 switch (menuType) { 385 case 'mainMenu': 386 if (parent.parent && parent.depth > depth) { 387 parent.setExpanded(true); 388 } 389 390 break; 391 392 case 'subMenu': 393 if (parent.parent) { 394 parent.setExpanded(true); 395 parent.setChildrenVisible(true); 396 } 397 398 break; 399 400 case 'mobileMenu': 401 if (parent.parent) { 402 parent.setExpanded(true); 403 } 404 405 break; 406 } 407 }); 408 409 switch (menuType) { 410 case 'mainMenu': 411 getElementsAtDepth(data, depth).forEach(element => { 412 413 if (element.parentOfActive) { 414 element.setExpanded(true); 415 } 416 417 // element.setVisible(true); 418 }); 419 420 break; 421 422 case 'subMenu': 423 getElementsAtDepth(data, depth + 1).forEach(element => { 424 element.setVisible(true); 425 }); 426 427 break; 428 } 429} 430 431export function closeOpenMenu(data) { 432 data.parent.children.filter(child => child.expanded === true).forEach(child => { 433 child.setExpanded(false); 434 child.setChildrenVisible(false); 435 }); 436} 437 438export function closeMenu(data) { 439 data.setExpanded(false); 440 data.setChildrenVisible(false); 441 442 if (document.querySelector('.hw_menu__mobile_button__search')) { 443 const mobileSearchButton = document.querySelector('.hw_menu__mobile_button__search'); 444 445 if (mobileSearchButton.getAttribute('aria-pressed') === 'true') { 446 mobileSearchButton.setAttribute('aria-pressed', false); 447 mobileSearchButton.nextElementSibling.setAttribute('aria-expanded', false); 448 } 449 } 450 451 document.body.classList.remove('hw_menu__open'); 452}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.