kulturbanause Blog

Responsive Design, WordPress, Konzeption, HTML, CSS, JS & UX/UI …

WordPress: Beiträge via AJAX nachladen (inklusive Fallback)

Um auf einer Übersichtsseite weitere Beiträge zu erreichen, ist die Verwendung eines »Load More«-Buttons eine beliebte Methode. Hierbei werden via JavaScript (AJAX) weitere Beiträge unter den zuvor angezeigten Beiträgen geladen, ohne dass die gesamte Unterseite neu geladen werden muss. Wenn JavaScript deaktiviert ist oder nicht geladen werden kann, ist es sinnvoll, als Fallback die klassische Pagination zu verwenden.

Workshops & Schulungen von kulturbanause

Intensive Trainings mit hohem Praxisbezug.

Online-Schulungen (remote per Video)

Inhouse-Schulungen

AJAX in WordPress

WordPress ist bereits von Hause aus mit einer AJAX-Funktionalität ausgestattet, die sich in der WP-Core-Datei admin-ajax.php befindet und durch Action-Hooks auch für besucherseitige AJAX-Anfragen benutzt werden kann. Auf diese Funktionalität greifen wir zurück, um einen »Load more«-Button unter unseren Loop zu integrieren. Hierbei spielt es keine Rolle, ob es sich um den Standard-Loop von WordPress oder um einen Custom-Loop (wp_query) handelt. Für beide Varianten haben wir ein Code-Beispiel erstellt.

Standard-Loop für AJAX-»Load more« vorbereiten

Als Basis für die Nachladen-Funktion verwenden wir die Funktion paginate_links() von WordPress. Dadurch berücksichtigen wir direkt das spätere Fallback, sollte die AJAX-Funktion aufgrund von JavaScript-Problemen nicht greifen. Somit verfolgen wir hier das Prinzip von »Progressive Enhancement«.
An die Pagination übergeben wir nun drei data-Attribute, die wir später mithilfe von JavaScript auslesen:

  • data-query: beinhaltet die Query-Variablen
  • data-maxpages: gibt an, auf wie viele Seiten der Loop aufgeteilt ist
  • data-current: gibt an, auf welcher Seite im Loop wir uns aktuell befinden
<?php
// Standard-Loop
if ( have_posts() ) {
  while ( have_posts() ) { the_post();
    get_template_part('template-parts','teaser');
  }

  // Pagination
  if($wp_query->max_num_pages > 1) {
    if($wp_query->query_vars["paged"] == 0) {
      $current_page = 1;
    } else {
      $current_page = $wp_query->query_vars["paged"];
    }
    echo '<div class="pagination" data-query="'.htmlspecialchars(json_encode($wp_query->query_vars)).'" data-maxpages="'.htmlspecialchars(json_encode($wp_query->max_num_pages)).'" data-current="'.$current_page.'">'.paginate_links(array('total' => $wp_query->max_num_pages)).'</div>';
  }
}
?>

WP Query für AJAX-»Load more« vorbereiten

Der grundsätzliche Aufbau bei einem Custom-Loop bleibt identisch mit dem oben beschrieben Aufbau, jedoch müssen wir hier noch drei weitere Variablen erzeugen, um die Pagination mit den richtigen Informationen zu versorgen. Weitere Informationen dazu auch in unserem Artikel: WordPress: WP_Query mit Pagination.

<?php
// Custom Loop
$count = get_option('posts_per_page', 10);
$paged = get_query_var('paged') ? get_query_var('paged') : 1;
$offset = ($paged - 1) * $count;

$args = array(
  'post_type' => 'post',
  'posts_per_page' => $count,
  'paged' => $paged,
  'offset' => $offset,
);
$wp_query = new WP_Query( $args );
if ( $wp_query->have_posts() ) {
  while ( $wp_query->have_posts() ) { $wp_query->the_post();
    get_template_part('template-parts','teaser');
  }

  // Pagination
  if($wp_query->max_num_pages > 1) {
    if($wp_query->query_vars["paged"] == 0) {
      $current_page = 1;
    } else {
      $current_page = $wp_query->query_vars["paged"];
    }
    echo '<div class="pagination" data-query="'.htmlspecialchars(json_encode($wp_query->query_vars)).'" data-maxpages="'.htmlspecialchars(json_encode($wp_query->max_num_pages)).'" data-current="'.$current_page.'">'.paginate_links(array('total' => $wp_query->max_num_pages)).'</div>';
  }
}
wp_reset_postdata();

AJAX-Script integrieren und Variablen übergeben

Nachdem wir den Loop vorbereiten haben, erstellen wir eine JavaScript-Datei, die wir in unserem Beispiel loadmore.js nennen und in die anschließend unsere Nachladen-Funktion geschrieben wird.

Mit Hilfe von wp_localize_script() übergeben wir drei Variablen an unser Script, die wir später benötigen:

  • ajaxurl: die URL der WordPress-AJAX-Datei
  • buttontxt: der Text für unseren »Load more«-Button
  • buttonload: der »Loading«-Text für den »Load more«-Button

Wir verwenden diese Methode unter anderem dafür, um später die Button-Beschriftung mit Hilfe von Sprachdateien übersetzbar zu machen.

Folgenden Code schreiben wir nun in unsere functions.php oder in ein seitenspezifisches Plugin.

function kb_load_scripts() {
  wp_enqueue_script('jquery');

  wp_register_script( 'kb_load_more', get_stylesheet_directory_uri() . '/loadmore.js', array('jquery') );

  wp_localize_script('kb_load_more', 'kb_string', array(
    'ajaxurl' => admin_url('admin-ajax.php'),
    'buttontxt' => __('Load more','kb-theme'),
    'buttonload' => __('Loading ...','kb-theme'),
  ));

  wp_enqueue_script( 'kb_load_more' );
}
add_action( 'wp_enqueue_scripts', 'kb_load_scripts' );

Bitte beachtet, dass wir für unser Beispiel zusätzlich jQuery benötigen, welches aber in WordPress von Hause aus an Bord ist.
AJAX funktioniert grundsätzlich natürlich auch mit »purem« JS.

AJAX-Funktion in JavaScript schreiben

Als Erstes werden alle HTML-Elemente mit der Klasse .page-numbers mit Ausnahme von .next aus dem DOM entfernt und der Text des Links mit der CSS-Klasse .next mit der zuvor übergebenen Variabel buttontxt ersetzt. Dies wird der »Load more«-Button.

Via Klick auf den »Load more«-Button lesen wir nun die data-Attribute aus der Pagination aus, speichern sie in Variablen und übergeben sie an data. Zusätzlich vergeben wir den action-Name kb_load_more, mit dem wir später die AJAX-Handler aufrufen.

Bevor der AJAX-Call beginnt, ändern wir den Button-Text mit der zuvor übergeben Variable zum »Loading«-Text. Nach erfolgreicher Ausführung des Calls wird die Seitenzahl »hochgezählt«, die Inhalte oberhalb der Pagination ausgespielt und der Button-Text wieder zurückgeändert.

Dem DOM werden also alle Inhalte hinzugefügt, die sich eigentlich auf der nachkommenden Seite innerhalb der Pagination befinden würden.

Wenn die aktuelle Seitenzahl gleich der maximalen Seitenzahl ist, wird der Button gänzlich entfernt, da es keine Inhalte mehr zum Nachladen gibt und der Button somit überflüssig ist.

jQuery(function ($) {
  $('.pagination .page-numbers').not('.next').remove();
  $('.pagination .next').html(kb_string.buttontxt);

  $(document).on('click','.pagination .next',function(e) {
    e.preventDefault();
    var query = JSON.stringify($(this).closest('.pagination').data('query'));
    var maxpages = $(this).closest('.pagination').data('maxpages');
    var current = parseInt($(this).closest('.pagination').data('current'));

    var button = $(this),
      data = {
        'action': 'kb_load_more',
        'query': query,
        'page' : current,
      };

    $.ajax({
      type: 'POST',
      url: kb_string.ajaxurl,
      data: data,
      beforeSend: function(xhr) {
        button.text(kb_string.buttonload);
      },
      success: function(data) {
        current++;
        button.closest('.pagination').before(data);
        button.closest('.pagination').data('current',current);
        button.text(kb_string.buttontxt);
        if ( current == maxpages ) {
          button.remove();
        }
      },
    });
  });
});

Richtige Inhalte ausgeben mit dem AJAX-Handler

Zu guter Letzt müssen wir dem AJAX-Call noch mitteilen, welche Inhalte er aus der Datenbank laden soll und wie er sie ausspielen soll. Hierfür benötigen wir den AJAX-Handler, den wir in unsere functions.php oder in ein seitenspezifisches Plugin schreiben. Dieser enthält einen einfachen Loop. Die Argumente für den Loop kommen aus dem AJAX-Call. Da Handler und Funktion miteinander »verknüpft« sind, müssen die WordPress-Actions mit der im Call angegebenen Actions übereinstimmen:

  • wp_ajax_kb_load_more: für eingeloggte Personen
  • wp_ajax_nopriv_kb_load_more: für nicht eingeloggte Personen
function kb_load_more_handler(){
  $args = json_decode( stripslashes( $_POST['query'] ), true );
  $args['paged'] = $_POST['page'] + 1;
  $args['post_status'] = 'publish';

  query_posts( $args );

  if( have_posts() ) {
    while( have_posts() ) { the_post();
      get_template_part('template-parts','teaser');
    }
  }
  die;
}
add_action('wp_ajax_kb_load_more', 'kb_load_more_handler');
add_action('wp_ajax_nopriv_kb_load_more', 'kb_load_more_handler');

Jetzt bist du gefragt!

Hast du Anregungen, Ergänzungen, einen Fehler gefunden oder ist dieser Beitrag nicht mehr aktuell? Dann freuen wir uns auf deinen Kommentar.

Du kannst diesen Beitrag natürlich auch weiterempfehlen. Wir sind dir für jede Unterstützung dankbar!

Unterstützung bei WordPress-Projekten

Unsere WordPress Agentur ist auf die Entwicklung maßgeschneiderter WordPress-Themes und -Websites spezialisiert. Wenn du Unterstützung bei der Planung, Gestaltung und Entwicklung eines Projekts benötigst, helfen wir gerne weiter.
WordPress-Leistungsangebot →

Das könnte dich auch interessieren

2 Kommentare

  1. Ferdal

    Verfasst am 31. März 2021 um 10:01 Uhr.

    Vielen Dank für den inspirierenden Artikel. Ich habe auf meiner Website mehrere custom querries. Muss ich den Ajax-Handler für jeden querry seperat anpassen, da ihr im Artikel schreibt: „Da Handler und Funktion miteinander »verknüpft« sind, müssen die WordPress-Actions mit der im Call angegebenen Actions übereinstimmen“. Wenn ja, frage ich mich, wie der Ajax-Handler die unterschiedlichen custom-querries unterscheidet. Im Ajax-Handler-Code steht nur: query_posts( $args );
    Würde mich über eine Antwort und eure Rückmeldung hierzu sehr freuen.
    Liebe Grüße

    • Robert Menzel

      Verfasst am 1. April 2021 um 17:14 Uhr.

      Hallo Ferdal,
      du musst den Ajax-Handler nicht für jeden Query anpassen.
      Durch die Angabe von data-query, data-maxpages, data-current übergibst du alle relevanten Daten an das Script, die benötigt werden um die richten Inhalte nachzuladen.

      Viele Grüße
      Robert

Kommentar verfassen

Dieser Blog lebt vom Feedback der Besucher! Also los, mach mit!
Bitte habe Verständnis dafür, dass Kommentare die mit dem Inhalt dieses Beitrags nichts zu tun haben, gelöscht werden.