Last active
October 29, 2022 05:40
-
-
Save thisbit/70887b3fc4e5e1283b6314f1fb23d504 to your computer and use it in GitHub Desktop.
Single query multiple loop with php an js filtering feature, with layouts with generatepress
This file contains 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
<?php /** | |
* Enqueue the assets for the filtering staff feature | |
*/ | |
function apuri_staff_filtering_assets() { | |
if ( is_page( array( 'nastavnici', 'lecturers' ) ) ) : | |
wp_enqueue_style( 'apuri-staff-filtering-assets', 'your plugin root' . 'assets/filter_and_search.css', false, '3.0.0.', 'all' ); | |
wp_enqueue_script( 'apuri-staff-filtering-assets', 'your plugin root' . 'assets/filtering_and_search.js', false, '3.0.0.', 'all' ); | |
endif; | |
} | |
add_action( 'wp_enqueue_scripts', 'apuri_staff_filtering_assets' ); |
This file contains 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
<?php | |
/** | |
* Template Name: Apuri Replace Content Template | |
* @package WordPress | |
* @subpackage GeneratePress | |
* Template Post Type: page | |
*/ | |
if ( ! defined( 'ABSPATH' ) ) { | |
exit; // Exit if accessed directly. | |
} | |
get_header(); | |
do_action( 'apuri_replace_content' ); | |
get_footer(); |
This file contains 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
.is-hidden { | |
display: none !important; | |
} | |
.grid { | |
margin-left: -2rem; | |
} | |
.grid h2 { | |
margin-left: 2rem; | |
} | |
.grid div { | |
display: flex; | |
flex-wrap: wrap; | |
justify-content: flex-start; | |
box-sizing: border-box; | |
} | |
.staff { | |
margin-bottom: 2rem; | |
} | |
.hentry { | |
margin-left: 2rem; | |
max-width: calc(25% - 2rem); | |
box-sizing: border-box; | |
} | |
.dynamic-featured-image { | |
width: 100%; | |
object-fit: cover; | |
} | |
.post-image { | |
min-width: 300px; | |
min-height: 300px; | |
} | |
.post-image img { | |
width: 100%; | |
height: 100%; | |
object-fit: cover; | |
} | |
.filter, .reset-filters { | |
cursor: pointer; | |
user-select: none; | |
display: flex; | |
width: 100%; | |
justify-content: space-between; | |
align-items: center; | |
padding: 1rem 0; | |
} | |
.filter::after, | |
.reset-filters::after { | |
content: ""; | |
display: block; | |
height: .8rem; | |
width: .8rem; | |
background-color: #222; | |
border: 3px solid #222; | |
border-radius: .4rem; | |
} | |
.filter:hover::after, | |
.reset-filters:hover::after, | |
.filter.active::after, | |
.reset-filters.active::after { | |
background-color: #fff; | |
border: 3px solid #FF5E5E; | |
} | |
.search-form.vanilla { | |
flex-wrap: wrap; | |
} | |
.search-form.vanilla .search-reset { | |
flex: 1; | |
margin-top: 1em; | |
} |
This file contains 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
/** | |
* Script Name: JS Live Search and Filter feature | |
* Description: This script provides Vanilla JS filtering and search capability, for a CPT listing Page. It has a fallback to PhP filter for browsers that do not support JS. The JS filter has no Depemndencies but it is limited to filtering and searching elements allreadz in the DOM. It does not fetch anything from DB. The advantage of this filter is speed and UX friendlyness. | |
* Original idea and code is from here @link https://codepen.io/hilmanski/pen/XWgZYYp?editors=1010 which I extended with fallback and filtering. | |
* | |
* TOC | |
* - DOM ELEMENTS | |
* -- utils | |
* - FUNCTION DEFINITIONS | |
* -- Toggle Section Headers | |
* -- Live search | |
* -- Filters | |
* - FUNCTION EXCUTIONS | |
* -- Filters | |
* -- Search | |
*/ | |
window.addEventListener( "DOMContentLoaded", () => { // wait for DOM loaded | |
/******************** DOM ELEMENTS *******************/ | |
const jsOrNot = Array.from(document.querySelectorAll( ".js-or-not" )); | |
const skipLink = document.querySelector( ".skip-link" ); | |
const primaryNavLinks = document.querySelectorAll( ".inside-navigation a" ); | |
const homeLinks = document.querySelectorAll( ".site-branding a" ); // ovo cemo maknuti na pravom sajtu | |
const cards = document.querySelectorAll( ".hentry" ); | |
const searchInput = document.getElementById( "searchbox" ); | |
const searchReset = document.querySelector( ".search-reset" ); | |
const filterButtons = document.querySelectorAll( ".filter" ); | |
const resetFilters = document.querySelector( ".reset-filters" ); | |
// utils | |
jsOrNot.forEach( element => element.classList.toggle( "is-hidden" ) ); | |
skipLink.setAttribute( "tabindex", 1 ); | |
homeLinks.forEach( element => element.setAttribute( "tabindex", 1 ) ); // maknuti na pravom sajtu | |
primaryNavLinks.forEach( element => element.setAttribute( "tabindex", 1 ) ); | |
/******************** FUNCTION DEFINITIONS *******************/ | |
/** | |
* Toggle Section Headers based on having or not having Content in | |
*/ | |
function toggleSectionHeadings() { | |
const sections = document.querySelectorAll( ".staff" ); | |
sections.forEach( (section) => { | |
if ( section.querySelectorAll(".cards .is-visible").length <= 0 ) { | |
section.classList.add( "is-hidden" ); | |
} else { | |
section.classList.remove( "is-hidden" ); | |
} | |
}, false ); | |
} | |
/** | |
* Live search shows and hides cards | |
*/ | |
function liveSearch() { | |
let searchQuery = document.getElementById( "searchbox" ).value; | |
//Use innerText if all contents are visible | |
//Use textContent for including hidden elements | |
for ( let i = 0; i < cards.length; i++ ) { | |
if ( cards[i].textContent.toLowerCase().includes(searchQuery.toLowerCase()) ) { | |
cards[i].classList.remove( "is-hidden" ); | |
cards[i].classList.add( "is-visible" ); | |
toggleSectionHeadings() | |
} else { | |
cards[i].classList.add( "is-hidden" ); | |
cards[i].classList.remove( "is-visible" ); | |
toggleSectionHeadings() | |
} | |
} | |
} | |
/** | |
* Filters show and hide the cards too | |
*/ | |
function filterCards( katedra ) { | |
for (let i = 0; i < cards.length; i++) { | |
if ( cards[i].classList.contains( katedra ) ) { // if you belong to my collection | |
cards[i].classList.remove( "is-hidden" ); | |
cards[i].classList.add( "is-visible" );// do not be | |
toggleSectionHeadings(); | |
} else if ( ! cards[i].classList.contains( katedra ) ) { // if you are not in my collection | |
cards[i].classList.add( "is-hidden" ); // please hide | |
cards[i].classList.remove( "is-visible" ); | |
toggleSectionHeadings(); | |
} | |
// if one clicks on filter reset, reveal all cards | |
resetFilters.addEventListener( "click", () => { | |
cards[i].classList.remove( "is-hidden" ); | |
cards[i].classList.add( "is-visible" ); | |
toggleSectionHeadings(); | |
}, false ); | |
resetFilters.addEventListener( "keyup", ( event ) => { | |
if ( event.key === "Enter" ) { | |
cards[i].classList.remove( "is-hidden" ); | |
cards[i].classList.add( "is-visible" ); | |
toggleSectionHeadings(); | |
} | |
}, false ); | |
} | |
} | |
/******************** FUNCTION EXCUTIONS *******************/ | |
// Filters | |
for ( let i = 0; i < filterButtons.length; i++ ) { | |
filterButtons[i].addEventListener( "click", () => { | |
filterCards( "apuri_katedra-" + filterButtons[i].id ); | |
searchInput.value = ""; // also clean search text when using filters | |
let ifActive = Array.from(document.querySelectorAll( ".active" )); | |
ifActive.forEach( element => element.classList.remove( "active" )); | |
filterButtons[i].classList.toggle('active'); | |
}, false ); | |
filterButtons[i].addEventListener( "keyup", ( event ) => { | |
if ( event.key === "Enter" ) { | |
filterCards( "apuri_katedra-" + filterButtons[i].id ); | |
searchInput.value = ""; // also clean search text when using filters | |
let ifActive = Array.from(document.querySelectorAll( ".active" )); | |
ifActive.forEach( element => element.classList.remove( "active" ) ); | |
filterButtons[i].classList.toggle('active'); | |
} | |
}, false ); | |
} | |
// Search | |
let typingTimer; | |
let typeInterval = 500; | |
searchInput.addEventListener( "keyup", () => { | |
clearTimeout( typingTimer ); | |
typingTimer = setTimeout( liveSearch, typeInterval ); | |
}); | |
searchReset.addEventListener( "keyup", () => { searchInput.value = ""; }); | |
}, false ); // end the on DOM load eventlistener |
This file contains 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
<?php | |
/** | |
* Grid of cpts with filtering, js filter is the better, but there is fallback to php filters | |
* @notice this markup is optimized for GeneratePress and should be adapted for use with other themes | |
*/ | |
if ( ! defined( 'ABSPATH' ) ) { | |
exit; // Exit if accessed directly. | |
} | |
function apuri_staff_filters_and_loops() { | |
ob_start(); | |
// Get katedra terms | |
$katedra_terms = get_terms( | |
array( | |
'taxonomy' => 'apuri_katedra', | |
'hide_empty' => true, | |
) | |
); | |
// fallback filters | |
if ( isset($_POST['katedra']) && ! empty( $_POST['katedra'] ) ) { | |
$katedra = esc_html__( $_POST['katedra'] ); | |
} else { | |
$katedra = ( get_locale() == 'en_US' ) ? array( 'printmaking', 'painting', 'intermedia', 'sculpture', 'theory', 'drawing', 'acting' ) : array( 'grafika', 'slikarstvo', 'intermedija', 'kiparstvo', 'teorija', 'crtanje', 'gluma' ); | |
} if ( isset($_POST['order']) && ! empty( $_POST['order'] ) ) { | |
$order = esc_html__( $_POST['order'] ); | |
} else { | |
$order = esc_html__( 'ASC' ); | |
} | |
?> | |
<div class="widget-area sidebar is-left-sidebar" id="lef-sidebar"> | |
<div class="inside-left-sidebar"> | |
<!-- FALLBACK FILTERS FORM --> | |
<form class="js-or-not widget inner-padding widget_block widget_search" action="<?php esc_url( the_permalink() ); ?>" method="post" id="nastavnici-fallback-filter" onchange="document.getElementById('nastavnici-fallback-filter').submit();"> <fieldset id="filters-2"> | |
<label><?php _e( 'Filtriraj' ); ?></label> | |
<a href="<?php esc_url( the_permalink() ); ?>"><?php _e('sve') ?></a> | |
<?php | |
foreach ( $katedra_terms as $katedra_term ) : | |
// escaping | |
$kslug = esc_html__( $katedra_term->slug ); | |
$kname = esc_html__( $katedra_term->name ); | |
if ( ! empty( $kslug ) ) : ?> | |
<label for="<?php echo $kslug; ?>"><input type="radio" name="katedra" id="<?php echo $kslug; ?>" value="<?php echo $kname; ?>" <?php | |
echo ( $kslug == $katedra ) ? 'checked' : ''; | |
?> /> <?php echo $kname; ?></label> | |
<?php | |
endif; | |
endforeach; | |
?> | |
<select name="order" id="order" > | |
<option value="ASC" | |
<?php echo ( 'ASC' == $order ) ? 'selected="selected"' : ''; ?> | |
onchange="document.getElementById('nastavnici-fallback-filter').submit()" >ASC</option> | |
<option value="DESC" | |
<?php echo ( 'DESC' == $order ) ? 'selected="selected"' : ''; ?> | |
onchange="document.getElementById('nastavnici-fallback-filter').submit()" >DESC</option> | |
</select> | |
<input type="hidden" value="filter" > | |
</fieldset> | |
</form> | |
<!-- END OF FALLBACK FILTERS FORM --> | |
<!-- JS SEARCH AND FILTERS BLOCK --> | |
<nav class="js-or-not is-hidden widget inner-padding widget_block widget_search" aria-label="<?php _e( 'Sekundarni izbornik' ); ?>" role="navigation" itemtype="https://schema.org/SiteNavigationElement" itemscope> | |
<ul class="secondary-menu"> | |
<li> | |
<a role="link" tabindex="2" class="reset-filters active" id="reset" ><?php _e( 'Sve' ); ?></a> | |
</li> | |
<?php | |
foreach ( $katedra_terms as $katedra_term ) { | |
$kslug = $katedra_term->slug; | |
$kname = $katedra_term->name; | |
if ( ! empty( $kslug ) ) { ?> | |
<li><a | |
tabindex="2" | |
class="filter" | |
role="link" | |
id="<?php echo $kslug; ?>" | |
/> <?php echo $kname; ?></a></li> | |
<?php | |
} | |
} | |
?><li > | |
<form class="search-form vanilla" role="search" aria-label="<?php _e( 'Lokalna pretraga stranice' ); ?>" > | |
<input class='input' tabindex="2" type="search" id="searchbox" placeholder="<?php _e( 'Traži' ); ?>"> | |
<input type="hidden" tabindex="2" class="search-reset" value="<?php _e( 'Poništi' ); ?>"> | |
</form> | |
</li> | |
</ul> | |
</nav> | |
<!-- JS SEARCH AND FILTERS BLOCK --> | |
</div> | |
</div> | |
<!-- JS QUERY AND LOOPS BLOCK --> | |
<div <?php generate_do_attr( 'content' ); ?>> | |
<main <?php generate_do_attr( 'main' ); ?>> | |
<div class="inside-article"> | |
<?php | |
$args = array( // single args of the single query | |
'post_type' => array ( 'apuri_osoblje' ), | |
'order' => $order, | |
'orderby' => 'title', | |
'tax_query' => array( | |
array( | |
'taxonomy' => 'apuri_katedra', | |
'field' => 'slug', | |
'terms' => $katedra, | |
'operator' => 'IN', | |
), | |
), | |
); | |
// global $post; | |
$zaposlenici = 'da'; | |
$query = new WP_query ( $args ); // single query | |
$theme = wp_get_theme(); // we test for theme used so we can control templates differently | |
?> | |
<section class="zaposlanici staff grid"> | |
<h2 class="section-heading"><?php _e('Zaposlenici'); ?></h2> | |
<div class="cards"> | |
<?php | |
if ( $query->have_posts() ) : // master if | |
// loop one | |
while ( $query->have_posts() ) : $query->the_post(); | |
if ( get_post_type() === 'apuri_osoblje' && has_term( $zaposlenici, 'apuri_zaposlenici' ) ) : ?> | |
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?> <?php generate_do_microdata( 'article' ); ?>> | |
<?php do_action( 'apuri_staff_card' ); ?> | |
</article> | |
<?php | |
endif; | |
endwhile; | |
rewind_posts(); | |
?> | |
</div> | |
</section> | |
<section class="suradnici staff grid"> | |
<h2 class="section-heading"><?php _e('Suradnici'); ?></h2> | |
<div class="cards"> | |
<?php | |
// loop two | |
while ( $query->have_posts() ) : $query->the_post(); | |
if ( get_post_type() === 'apuri_osoblje' && ! has_term( $zaposlenici, 'apuri_zaposlenici' ) ) : ?> | |
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?> <?php generate_do_microdata( 'article' ); ?>> | |
<?php do_action( 'apuri_staff_card' ); ?> | |
</article> | |
<?php | |
endif; | |
endwhile; | |
rewind_posts(); // this is crucial | |
?> | |
</div> | |
</section> | |
<section class="static-content"> | |
<?php | |
the_post(); | |
the_content(); | |
?> | |
</section> | |
</div> | |
</main> | |
</div> | |
<?php endif; // end master if | |
$output = ob_get_clean(); | |
return $output; | |
} | |
function apuri_add_staff_grid_to_page() { | |
echo $nastavnici = ( is_page( array( 'nastavnici', 'lecturers' ) ) ) ? apuri_staff_filters_and_loops() : ''; | |
} | |
/** | |
* We inject the staff content filtering page in a dedicated template using a custom hook | |
* @custom_hook do_action( 'apuri_replace_content' ); | |
* @custom_page_template should have this only "get_header(); do_action( 'apuri_replace_content' ); get_footer();" | |
*/ | |
add_action( 'apuri_replace_content', 'apuri_add_staff_grid_to_page'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment