1/** 2 * @file 3 * Scroll search results into view. 4 */ 5(function (Drupal) { 6 let searchScrollInitialized = false; 7 8 Drupal.behaviors.tsLvhnDrawerSearch = { 9 attach: function (context, settings) { 10 if (!searchScrollInitialized) { 11 searchScrollInitialized = true; 12 initDrawerSearch(); 13 initSearch(); 14 } 15 } 16 }; 17})(Drupal); 18 19function initSearch() { 20 const searchInputs = [ 21 // Find a doctor - Home page. 22 document.querySelector('.fad-initial-simple-home .form-item-keys input[type="text"]'), 23 // Find a doctor - Detail. 24 document.querySelector('.fad-initial-simple input[type=text][id*="visible-keys"]'), 25 // Find a location. 26 document.querySelector('.fal-initial input[type=text][id*="visible-keys"]'), 27 // Find a service. 28 document.querySelector('#lvhn-search-fas-initial input[type="text"]'), 29 // Browse all services. 30 document.querySelector('#lvhn-search-fas-refine-results-top input[type="text"]'), 31 // Find a job. 32 document.querySelector('.faj-initial-simple input[type=text][id*="visible-keys"]'), 33 ]; 34 35 if (!isMobileDevice()) return; 36 37 searchInputs.forEach((searchInput) => { 38 if (searchInput) { 39 searchInput.addEventListener('focus', function () { 40 // Use setTimeout to ensure scrolling is not interrupted. 41 setTimeout(() => { 42 searchInput.scrollIntoView({ 43 behavior: "smooth", 44 block: "start", 45 }); 46 }, 500); 47 }); 48 } 49 }); 50} 51 52/** 53 * Trigger scrolling when search results are rendered for drawer search. 54 */ 55function initDrawerSearch() { 56 const searchResults = document.querySelector('#suggested-result'); 57 const searchInput = document.querySelector('#nav-search-keys'); 58 const panelTrigger = document.querySelector('.search-toggle'); 59 const searchForm = document.querySelector('.search-panel .search-form'); 60 let observerSearchResults; 61 62 if (!searchResults || !searchInput || !panelTrigger || !searchForm) return; 63 64 // Add mutation observer on search trigger that checks if it has tabindex="-1". 65 // If tabindex="-1" is set we know the search drawer is visible. 66 const observerSearchTrigger = new MutationObserver(function (mutations) { 67 mutations.forEach(function (mutation) { 68 if (mutation.attributeName === 'tabindex') { 69 if (panelTrigger.getAttribute('tabindex') === '-1') { 70 // Attach oberver when drawer open. 71 observerSearchDrawerMutations(); 72 } else { 73 74 // Disconnect oberver when drawer closed. 75 observerSearchResults.disconnect(); 76 } 77 } 78 }); 79 }); 80 81 // Observe the search trigger for changes. 82 observerSearchTrigger.observe(panelTrigger, { attributes: true }); 83 84 /** 85 * Observes mutations in the search results and triggers scrolling when new nodes are added. 86 * Resets the scroll state when the search input has fewer than 3 characters or when backspacing. 87 */ 88 function observerSearchDrawerMutations() { 89 let hasScrolled = false; 90 91 if (!searchResults || !searchForm || !searchInput) return; 92 93 // Add mutation observer to search results that checks for changes in child nodes. 94 observerSearchResults = new MutationObserver(function (mutations) { 95 mutations.forEach(function (mutation) { 96 if (mutation.addedNodes.length && !hasScrolled) { 97 hasScrolled = true; 98 99 // Use setTimeout to wait for the search results to be rendered. 100 setTimeout(() => { 101 // Only scroll on mobile devices. 102 const isMobile = isMobileDevice(); 103 if (!isMobile) return; 104 105 searchForm.scrollIntoView({ 106 behavior: "smooth", 107 block: "start", 108 }); 109 }, 500); 110 } 111 }); 112 }); 113 114 observerSearchResults.observe(searchResults, { childList: true }); 115 116 let searchInputCharacterCount = 0; 117 118 // If input has less than 3 characters or user is backspacing reset hasScrolled. 119 searchInput.addEventListener('keyup', function (event) { 120 121 // If fewer than 3 characters are entered reset hasScrolled. 122 // Results require 3 characters to be displayed. 123 if (event.target.value.length < 3) { 124 hasScrolled = false;
125 } 126 127 // If fewer characters are entered user is backspacing. Reset hasScrolled. 128 else if (event.target.value.length < searchInputCharacterCount) { 129 hasScrolled = false; 130 } 131 132 // Track the number of characters in the search input. 133 searchInputCharacterCount = event.target.value.length; 134 }); 135 } 136} 137 138/** 139 * Checks if the current device is a mobile device. 140 * 141 * @returns {boolean} - True if the device is a mobile device, false otherwise. 142 */ 143function isMobileDevice() { 144 return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent); 145}
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.