# AJAX filtrace pro Oxygen Repeater/Easy Posts bez přenačtení stránky

- Odkaz: https://tomas-shejbal.cz/wordpressito/ajax-filtrace-pro-oxygen-repeater-easy-posts-bez-prenacteni-stranky/
- Zveřejněno: 2025-08-06T08:02:46+02:00
- Autor: tom

# AJAX filtrace pro Oxygen Repeater/Easy Posts bez přenačtení stránky

Při práci s Oxygen Repeaterem/Easy Posts často potřebuju filtrovat obsah.

Standardně to znamená přenačtení celé stránky. Přiznejme si, že to uživatelsky není
ono.

Pojďme si ukázat, jak implementovat AJAX filtraci, která **zachová všechny výhody
Oxygen renderingu**, ale **bez nutnosti obnovovat stránku**.

_Poznámka: nejedná se o JS filtraci, která neumí pagination, ale o plnohodnotné 
řešení._ [Srovnání různých přístupů k filtraci v předchozím článku](https://tomas-shejbal.cz/wordpressito/porovnani-pristupu-k-filtraci-oxygen-repeateru-easy-posts).

![](https://tomas-shejbal.cz/wp-content/uploads/2025/07/wordpressito_repeater_filtrace.
webp)

_Datum poslední změny: 6. 8. 2025_

## Výchozí situace

Máme Oxygen Repeater, který zobrazuje _custom post type _(vlastní typ příspěvku)
s několika ACF, přes které chci filtrovat příspěvky. Pro výpis příspěvků používám
_Repeater_ s _Advanced Query._ Filtruju podle url parametru přes _meta query_ (přes
ACF políčka).

Utm parametr získávám z parametru `filter`. Například:

    ```wp-block-code
    <https://vase-stranka.cz/suppliers/?filter=furniture>
    ```

## Řešení

Řešení využívá JavaScript k zachycení kliknutí na filtrační tlačítko, aktualizaci
URL a výměnu obsahu _Repeateru_ bez přenačtení stránky.

_Poznámka: kódy níže jsou pro Repeater, ale pro Easy Posts se postup neliší. Drobné
změny jsou jen v JS, kde se musí zohlednit jiné třídy a selektory (Repater a Easy
Posts mají každý vlastní)._

**Takhle může vypadat adresářová struktura:**

    ```wp-block-code
    my-custom-plugin/
        ├── my-custom-plugin.php
        └── lib/
            └── js/
                └── filter-script.js
    ```

**Příklad načtení JS (JS uveden dále):**

    ```wp-block-code
    function register_filter_script() {
        if (is_page('suppliers')) {
            wp_enqueue_script(
                'suppliers-filter',  
                plugin_dir_url(__FILE__) . 'lib/js/filter-script.js', 
                array(), 
                '1.0.0', 
                true
            );
        }
    }
    add_action('wp_enqueue_scripts', 'register_filter_script');
    ```

**Takto může vypadat JS:**

    ```wp-block-code
    document.addEventListener('DOMContentLoaded', function() {
        const filterButtons = document.querySelectorAll('.filter-button');

        filterButtons.forEach(button => {
            button.addEventListener('click', function(e) {
                e.preventDefault();

                const filter = this.dataset.filter;
                const url = new URL(window.location);
                url.searchParams.set('filter', filter);

                window.history.pushState({}, '', url);

                const repeater = document.getElementById('dynamic_list');
                repeater.classList.add('loading');

                const tempContainer = document.createElement('div');
                tempContainer.style.display = 'none';
                document.body.appendChild(tempContainer);

                tempContainer.innerHTML = `<div class="oxy-dynamic-list" id="temp-list"></div>`;

                fetch(url.toString())
                    .then(response => response.text())
                    .then(html => {
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(html, 'text/html');
                        const newRepeater = doc.getElementById('dynamic_list');

                        if (newRepeater) {
                            repeater.innerHTML = newRepeater.innerHTML;
                        }

                        repeater.classList.remove('loading');  
                        tempContainer.remove();
                    })
                    .catch(error => {
                        console.error('Error:', error);
                        repeater.classList.remove('loading'); 
                        tempContainer.remove();
                    });
            });
        });
    });
    ```

**Filtrační tlačítka**

Samozřejmě nemusí jít o tlačítka. Můžou to být radio buttony (výběr jedné možnosti
z vícera), check boxy (zaškrtávátka) nebo třeba selecty (rozbalovací nabídka). Pro
tlačítka bude html například takovéto:

    ```wp-block-code
    <button class="filter-button" data-filter="furniture">Nábytek</button>
    <button class="filter-button" data-filter="inventory">Inventář</button>
    ```

**A trocha "fancy" animace při načítání. 😊**

    ```wp-block-code
    .oxy-dynamic-list {
        transition: opacity 0.3s ease-in-out;
    }

    .oxy-dynamic-list.loading {
        opacity: 0.5;
    }
    ```

## Jak to funguje?

 1. Zachytíme klik na filtrační tlačítko
 2. Aktualizujeme URL s novým parametrem filtru
 3. Pomocí `fetch` načteme celou stránku s novým filtrem
 4. Z vrácené stránky "vykostíme" jen obsah repeateru
 5. Aktualizujeme kontejner Repeateru o nově načtený obsah

## Výhody tohoto řešení

 1. **Jednoduchá implementace** - není potřeba vytvářet REST API endpoint
 2. **Využití existující logiky** - WP Query je definováno pouze v Oxygen Repeateru
 3. **Funguje stránkování** - server generuje správnou stránku s odpovídajícími daty
 4. **Zachování Oxygen funkcionality** - filtrační parametry a jiné nastavení v Oxygen
    Repeater/Easy Posts fungují normálně
 5. **Robustní řešení** - méně míst, kde se může něco pokazit
 6. **Sdílitelné URL** - odkazy s filtry vedou na stránku se volenou filtrací

## Závěr

Toto řešení představuje elegantní kompromis mezi uživatelskou přívětivostí (žádné
přenačítání stránky) a jednoduchostí implementace. Místo vytváření paralelní implementace
WP Query v REST API využívám existující Oxygen funkcionalitu, což vede k robustnějšímu
a udržitelnějšímu kódu.
