Last active
September 23, 2026 12:04
-
-
Save jmabbas/a0c73a2d4a10f8802e83cbf79770d586 to your computer and use it in GitHub Desktop.
GoTrek - Search box update as per input count
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * -------------------------------------------------------------------------- | |
| * GoTrek: Dynamic Hotel Filter by Location, Adults, and Children | |
| * -------------------------------------------------------------------------- | |
| */ | |
| // 1. Output hotel data attributes inside each product item in the loop | |
| add_action( 'woocommerce_before_shop_loop_item', 'gotrek_output_hotel_filter_meta', 1 ); | |
| add_action( 'woocommerce_shop_loop_item_title', 'gotrek_output_hotel_filter_meta', 1 ); | |
| if ( ! function_exists( 'gotrek_output_hotel_filter_meta' ) ) { | |
| function gotrek_output_hotel_filter_meta() { | |
| static $rendered_products = array(); | |
| global $product; | |
| if ( ! $product ) { | |
| return; | |
| } | |
| $product_id = $product->get_id(); | |
| if ( in_array( $product_id, $rendered_products, true ) ) { | |
| return; | |
| } | |
| $rendered_products[] = $product_id; | |
| // Fetch hotel group | |
| $hotel_group = function_exists( 'gotrek_get_field' ) | |
| ? gotrek_get_field( 'hotel_group', $product_id ) | |
| : ( function_exists( 'get_field' ) ? get_field( 'hotel_group', $product_id ) : null ); | |
| $location = ''; | |
| if ( ! empty( $hotel_group['location'] ) ) { | |
| $location = $hotel_group['location']; | |
| } elseif ( ! empty( $hotel_group['location_map']['address'] ) ) { | |
| $location = $hotel_group['location_map']['address']; | |
| } | |
| // Find both HIGHEST single-room capacity and TOTAL hotel capacity across all rooms | |
| $top_adults = 0; | |
| $top_children = 0; | |
| $total_adults = 0; | |
| $total_children = 0; | |
| $rooms_count = 0; | |
| if ( ! empty( $hotel_group['rooms'] ) && is_array( $hotel_group['rooms'] ) ) { | |
| $rooms_count = count( $hotel_group['rooms'] ); | |
| foreach ( $hotel_group['rooms'] as $room ) { | |
| $room_id = is_object( $room ) ? $room->ID : absint( $room ); | |
| $room_group = function_exists( 'gotrek_get_field' ) | |
| ? gotrek_get_field( 'room_group', $room_id ) | |
| : ( function_exists( 'get_field' ) ? get_field( 'room_group', $room_id ) : null ); | |
| $r_adults = isset( $room_group['max_adult'] ) ? absint( $room_group['max_adult'] ) : 0; | |
| $r_children = isset( $room_group['max_children'] ) ? absint( $room_group['max_children'] ) : 0; | |
| // Total capacity of hotel | |
| $total_adults += $r_adults; | |
| $total_children += $r_children; | |
| // Maximum single room capacity | |
| if ( $r_adults > $top_adults ) { | |
| $top_adults = $r_adults; | |
| } | |
| if ( $r_children > $top_children ) { | |
| $top_children = $r_children; | |
| } | |
| } | |
| } else { | |
| // Direct room product or simple product | |
| $room_group = function_exists( 'gotrek_get_field' ) | |
| ? gotrek_get_field( 'room_group', $product_id ) | |
| : ( function_exists( 'get_field' ) ? get_field( 'room_group', $product_id ) : null ); | |
| if ( ! empty( $room_group ) ) { | |
| $top_adults = isset( $room_group['max_adult'] ) ? absint( $room_group['max_adult'] ) : 0; | |
| $top_children = isset( $room_group['max_children'] ) ? absint( $room_group['max_children'] ) : 0; | |
| $total_adults = $top_adults; | |
| $total_children = $top_children; | |
| $rooms_count = 1; | |
| } | |
| } | |
| printf( | |
| '<span class="gotrek-hotel-filter-meta" style="display:none;" data-location="%s" data-max-adults="%d" data-max-children="%d" data-total-adults="%d" data-total-children="%d" data-rooms="%d"></span>', | |
| esc_attr( strtolower( trim( $location ) ) ), | |
| $top_adults, | |
| $top_children, | |
| $total_adults, | |
| $total_children, | |
| $rooms_count | |
| ); | |
| } | |
| } | |
| // 2. Client-Side Filter Script | |
| add_action( 'wp_footer', 'gotrek_hotel_filter_script', 99 ); | |
| if ( ! function_exists( 'gotrek_hotel_filter_script' ) ) { | |
| function gotrek_hotel_filter_script() { | |
| ?> | |
| <script type="text/javascript"> | |
| (function() { | |
| 'use strict'; | |
| function cleanStr(str) { | |
| return (str || '') | |
| .toString() | |
| .toLowerCase() | |
| .normalize('NFD') | |
| .replace(/[\u0300-\u036f]/g, '') | |
| .trim(); | |
| } | |
| function getCountValue(type) { | |
| // type: 'adult', 'child', 'room' | |
| var paramKey = (type === 'adult' ? 'adults' : (type === 'child' ? 'children' : 'rooms')); | |
| // 1. Check visible counter elements (updates immediately on click) | |
| var countEls = document.querySelectorAll(".js-counter[data-value-change*='" + type + "'] .js-count, .js-counter[data-value-change*='" + type + "'] .js-result"); | |
| for (var i = 0; i < countEls.length; i++) { | |
| var txt = countEls[i].textContent.trim(); | |
| if (txt !== '') { | |
| var num = parseInt(txt, 10); | |
| if (!isNaN(num)) return num; | |
| } | |
| } | |
| // 2. Check input fields | |
| var inputEls = document.querySelectorAll("input.js-count-" + type + ", input[name='" + paramKey + "'], input.input-" + (type === 'child' ? 'children' : type) + "-value"); | |
| for (var j = 0; j < inputEls.length; j++) { | |
| if (inputEls[j].value !== '') { | |
| var num = parseInt(inputEls[j].value, 10); | |
| if (!isNaN(num)) return num; | |
| } | |
| } | |
| // 3. Check text spans (e.g. "1 child" or "0 children") | |
| var spanEls = document.querySelectorAll("span.js-count-" + type); | |
| for (var k = 0; k < spanEls.length; k++) { | |
| var match = spanEls[k].textContent.match(/\d+/); | |
| if (match) { | |
| var num = parseInt(match[0], 10); | |
| if (!isNaN(num)) return num; | |
| } | |
| } | |
| // 4. Fallback to URL params | |
| var urlParams = new URLSearchParams(window.location.search); | |
| if (urlParams.has(paramKey)) { | |
| var num = parseInt(urlParams.get(paramKey), 10); | |
| if (!isNaN(num)) return num; | |
| } | |
| return 0; | |
| } | |
| function getLocationValue() { | |
| var locInput = document.querySelector("input.search, input.location-map, input.location-acf-field, .search-location input[type='text']"); | |
| if (locInput && locInput.value.trim() !== '') { | |
| return cleanStr(locInput.value); | |
| } | |
| var urlParams = new URLSearchParams(window.location.search); | |
| if (urlParams.has('location') && urlParams.get('location').trim() !== '') { | |
| return cleanStr(urlParams.get('location')); | |
| } | |
| return ''; | |
| } | |
| function filterHotels() { | |
| var searchLoc = getLocationValue(); | |
| var searchAdults = getCountValue('adult'); | |
| var searchChildren = getCountValue('child'); | |
| var searchRooms = getCountValue('room'); | |
| var metas = document.querySelectorAll('.gotrek-hotel-filter-meta'); | |
| if (!metas.length) return; | |
| var visibleCount = 0; | |
| metas.forEach(function(meta) { | |
| var li = meta.closest('li'); | |
| if (!li) return; | |
| var hotelLoc = cleanStr(meta.getAttribute('data-location') || ''); | |
| var maxAdults = parseInt(meta.getAttribute('data-max-adults') || '0', 10); | |
| var maxChildren = parseInt(meta.getAttribute('data-max-children') || '0', 10); | |
| var totalAdults = parseInt(meta.getAttribute('data-total-adults') || maxAdults.toString(), 10); | |
| var totalChildren = parseInt(meta.getAttribute('data-total-children') || maxChildren.toString(), 10); | |
| var roomsCount = parseInt(meta.getAttribute('data-rooms') || '1', 10); | |
| var effectiveMaxAdults = Math.max(maxAdults, totalAdults); | |
| var effectiveMaxChildren = Math.max(maxChildren, totalChildren); | |
| // 0. Always hide products with 0 guest/room capacity | |
| if (effectiveMaxAdults === 0 && roomsCount === 0) { | |
| li.style.setProperty('display', 'none', 'important'); | |
| return; | |
| } | |
| // 1. Location Check | |
| var locationMatch = true; | |
| if (searchLoc !== '') { | |
| var sLoc = searchLoc === 'bucharest' ? 'bucurest' : searchLoc; | |
| var pLoc = hotelLoc === 'bucharest' ? 'bucurest' : hotelLoc; | |
| locationMatch = (pLoc.indexOf(sLoc) !== -1 || sLoc.indexOf(pLoc) !== -1); | |
| } | |
| // 2. Capacity Check (Hide if requested count exceeds hotel capacity) | |
| var exceedsAdults = (searchAdults > 0 && searchAdults > effectiveMaxAdults); | |
| var exceedsChildren = (searchChildren > 0 && searchChildren > effectiveMaxChildren); | |
| var exceedsRooms = (searchRooms > 0 && roomsCount > 0 && searchRooms > roomsCount); | |
| var isMatch = locationMatch && !exceedsAdults && !exceedsChildren && !exceedsRooms; | |
| // 3. Apply Visibility | |
| if (isMatch) { | |
| li.style.removeProperty('display'); | |
| li.style.display = ''; | |
| // Clear any hardcoded or lingering display:none in style attribute | |
| if (li.getAttribute('style') && li.getAttribute('style').indexOf('display') !== -1) { | |
| var cleanedStyle = li.getAttribute('style') | |
| .replace(/display\s*:\s*none\s*!important\s*;?/gi, '') | |
| .replace(/display\s*:\s*none\s*;?/gi, '') | |
| .trim(); | |
| if (cleanedStyle) { | |
| li.setAttribute('style', cleanedStyle); | |
| } else { | |
| li.removeAttribute('style'); | |
| } | |
| } | |
| visibleCount++; | |
| } else { | |
| li.style.setProperty('display', 'none', 'important'); | |
| } | |
| }); | |
| // No results message handling | |
| var noResultsEl = document.querySelector('.gotrek-js-no-products'); | |
| var container = document.querySelector('.wp-block-woocommerce-product-collection, ul.products, .wp-block-query'); | |
| if (visibleCount === 0 && (searchLoc !== '' || searchAdults > 0 || searchChildren > 0 || searchRooms > 0)) { | |
| if (!noResultsEl && container) { | |
| noResultsEl = document.createElement('p'); | |
| noResultsEl.className = 'gotrek-js-no-products text-center my-4 font-weight-bold'; | |
| noResultsEl.textContent = 'No products found matching your selected location and guest capacity.'; | |
| container.parentNode.insertBefore(noResultsEl, container.nextSibling); | |
| } | |
| } else if (noResultsEl) { | |
| noResultsEl.remove(); | |
| } | |
| } | |
| // Initial runs | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', filterHotels); | |
| } else { | |
| filterHotels(); | |
| } | |
| window.addEventListener('load', filterHotels); | |
| // Debounce / delayed execution helper | |
| var timer = null; | |
| function triggerFilter() { | |
| filterHotels(); | |
| clearTimeout(timer); | |
| timer = setTimeout(filterHotels, 30); | |
| setTimeout(filterHotels, 100); | |
| setTimeout(filterHotels, 250); | |
| } | |
| // 1. Live click events on plus/minus buttons & counters | |
| ['click', 'mouseup', 'touchend'].forEach(function(evt) { | |
| document.addEventListener(evt, function(e) { | |
| if (e.target.closest('.js-plus, .js-minus, .js-up, .js-down, .js-counter, .counter-button-wrap, .searchMenu-guests, .search-submit')) { | |
| triggerFilter(); | |
| } | |
| }); | |
| }); | |
| // 2. Input / Change events on text fields and hidden inputs | |
| document.addEventListener('input', triggerFilter); | |
| document.addEventListener('change', triggerFilter); | |
| document.addEventListener('keyup', triggerFilter); | |
| // 3. MutationObserver on the guest counters and DOM | |
| var guestContainer = document.querySelector('.searchMenu-guests, .js-form-counters, .search-wrap'); | |
| if (guestContainer && window.MutationObserver) { | |
| var observer = new MutationObserver(function() { | |
| triggerFilter(); | |
| }); | |
| observer.observe(guestContainer, { | |
| childList: true, | |
| subtree: true, | |
| characterData: true, | |
| attributes: true | |
| }); | |
| } | |
| })(); | |
| </script> | |
| <?php | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment