1/** 2 * Navigation Extension frontend features: 3 * anchor-active-style. 4 * 5 * @since 1.13.0 6 * @see greyd.scrollObserver for details on the mechanic. 7 */ 8 9document.addEventListener( "DOMContentLoaded", function() { 10 11 if ( !greyd?.scrollObserver ) return; 12 13 function initNavigations() { 14 15 // get navigation blocks with anchor-active-style 16 var navBlocks = document.querySelectorAll('.wp-block-navigation.is-anchor-active-style'); 17 if ( navBlocks.length == 0 ) return; 18 19 navBlocks.forEach( ( wrapper, key ) => { 20 21 const setup = wrapper.dataset.anchoractive ? JSON.parse(wrapper.dataset.anchoractive) : {}; 22 if ( !setup.enable) return; 23 24 const activeLinks = wrapper.querySelectorAll('a.wp-block-navigation-item__content[href*="#"]'); 25 if ( activeLinks.length == 0 ) return; 26 27 // setup 28 let start = setup.start ? parseInt(setup.start) : 0; 29 let end = setup.end ? parseInt(setup.end) : 100; 30 if ( end < start ) { 31 let tmp = start; 32 start = end; 33 end = tmp; 34 } 35 const multiple = setup.multiple ? setup.multiple : ''; 36 const none = setup.none ? setup.none : ''; 37 38 let items = []; 39 activeLinks.forEach( ( link, key ) => { 40 const item = link.parentElement; 41 // get items href, anchor-id and anchor-element 42 const href = link.href; 43 const id = href.split('#')[1]; 44 const anchor = document.getElementById(id); 45 if ( anchor ) { 46 // console.log(item); 47 // console.log(href); 48 items.push( { item: item, anchor: anchor } ); 49 50 var simple = start == 0 && end == 100 && multiple == '' && none == ''; 51 if ( simple ) { 52 // simple observer (default setup) 53 greyd.scrollObserver.observeElement( 54 anchor, 55 ( target, event, inViewport ) => { 56 // toggle active class 57 if ( inViewport ) { 58 item.classList.add('current-menu-item'); 59 } 60 else { 61 item.classList.remove('current-menu-item'); 62 } 63 }, 64 'change' 65 ); 66 } 67 else { 68 // advanced observer 69 greyd.scrollObserver.observeElement( 70 anchor, 71 ( target, event, inViewport ) => { 72 73 // get offsets 74 var boundingClientRect = target.getBoundingClientRect(); 75 var targetTop = boundingClientRect.y; 76 var targetBottom = targetTop + boundingClientRect.height; 77 var endLine = (window.innerHeight / 100) * end; 78 var startLine = (window.innerHeight / 100) * start; 79 80 // get distance to center function 81 const getDistance = ( element ) => { 82 // get offsets 83 var bounds = element.getBoundingClientRect(); 84 var top = bounds.y; 85 var bottom = top + bounds.height; 86 var center = (endLine + startLine) / 2; 87 // var center = window.innerHeight / 2; 88 // calculate distance 89 var distance = 0; 90 if ( bottom < center && top < center ) { 91 // above center 92 distance = center - bottom; 93 } 94 if ( bottom > center && top > center ) { 95 // below center 96 distance = top - center; 97 } 98 return distance; 99 }; 100 101 // check if target is between start and end 102 var inside = targetBottom > startLine && targetTop < endLine; 103 104 // multiple: show closest to center 105 if ( inside && multiple == 'closest' ) { 106 107 // calculate this distance to center 108 var distance = getDistance(target); 109 // check if other item is active 110 items.forEach( (other) => { 111 // escape this and non active items 112 if ( other.item === item || !other.item.classList.contains('current-menu-item') ) return; 113 // calculate other distance 114 if ( getDistance(other.anchor) < distance ) { 115 // don't show this if other item is closer to center 116 inside = false;
117 } 118 } ); 119 120 } 121 122 // multiple: show only latest 123 if ( inside && !item.classList.contains('current-menu-item') && multiple == 'latest' ) { 124 125 // deactivate other elements 126 items.forEach( (other) => { 127 // escape this and non active items 128 if ( other.item === item || !other.item.classList.contains('current-menu-item') ) return; 129 other.item.classList.remove('current-menu-item'); 130 } ); 131 132 } 133 134 // toggle active class 135 if ( inside ) { 136 item.classList.add('current-menu-item'); 137 } 138 else { 139 item.wasActive = item.classList.contains('current-menu-item'); 140 if ( item.wasActive ) items.forEach( (other) => { 141 // escape this 142 if ( other.item === item ) return; 143 other.item.wasActive = false; 144 } ); 145 146 item.classList.remove('current-menu-item'); 147 } 148 149 // no active 150 if ( none != '' ) { 151 152 // check if any item is active 153 var anyActive = false; 154 var distances = []; 155 items.forEach( (other, index) => { 156 // remove keeper class 157 if ( other.item.classList.contains('keep-anchor-active') ) { 158 other.item.classList.remove('current-menu-item', 'keep-anchor-active'); 159 } 160 // check if item is active 161 if ( other.item.classList.contains('current-menu-item') ) { 162 anyActive = true; 163 } 164 else { 165 // get distance to center for closest/fallback 166 distances.push( { index: index, distance: getDistance(other.anchor) } ); 167 } 168 } ); 169 if ( !anyActive ) { 170 171 // keep active until next element enters 172 if ( none == 'keep' ) { 173 items.forEach( (other, index) => { 174 // check if item was active 175 if ( other.item.wasActive ) { 176 other.item.classList.add('current-menu-item', 'keep-anchor-active'); 177 anyActive = true; 178 } 179 } ); 180 } 181 182 // activate closest element 183 // fallback / none == 'closest' 184 if ( !anyActive ) { 185 // sort by distance to center 186 distances.sort((a, b) => a.distance - b.distance); 187 // add keeper class if no other item is active 188 items[distances[0].index].item.classList.add('current-menu-item', 'keep-anchor-active'); 189 } 190 191 } 192 193 } 194 195 }, 196 'always' 197 ); 198 199 // remove active class initially 200 // (observeElement 'always' does not trigger on inViewport false) 201 item.classList.remove('current-menu-item'); 202 } 203 } 204 } ); 205 206 } ); 207 208 console.log("navigation scripts loaded."); 209 } 210 211 initNavigations(); 212 213} );
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.