WordPress – woher weiß ich, ob ein Menüpunkt Kinder hat?
Lesezeit: 9 Minuten
kikito
Ich entwickle ein WordPress-Theme mit verschachtelten Untermenüs. Ich muss die Elemente ohne Kinder optisch von denen mit Kindern unterscheiden. Im Moment habe ich dieses Menü, aber das könnte sich ändern:
A
a1
a2
B
b1
b2
C
Wie Sie sehen können, haben A und B Kinder. C nicht – ich brauche es, um in der CSS-Ebene anders zu sein.
Idealerweise hätte ich gerne eine has-children Klasse in A und B, aber nicht in C.
Bisher habe ich es geschafft, eine „Menu Walker“-PHP-Klasse zu erstellen, die ich instanziieren und an die ich übergeben kann wp_nav_menu . Sein Konstruktor sieht so aus:
class My_Walker_Nav_Menu extends Walker_Nav_Menu {
function start_el(&$output, $item, $depth, $args) {
...
if(??? $item has children???) {
// I know what to do here
}
}
}
Also, wie erkenne ich, ob $item hat Kinder, oder ist ein Blatt?
BEARBEITEN: Diese Frage wurde von jemandem namens “keesiemeijer” in den WordPress-Foren beantwortet. Ich lasse dieses Kopfgeld verfallen, nur für den Fall, dass er es zurückfordern will. Andernfalls werde ich meine eigene Antwort als gültig markieren.
janw
Fügen Sie dies hinzu functions.php es wird die ‘Dropdown’-Klasse zu den Eltern hinzufügen
Neue Weise besser für Leistung
function menu_set_dropdown( $sorted_menu_items, $args ) {
$last_top = 0;
foreach ( $sorted_menu_items as $key => $obj ) {
// it is a top lv item?
if ( 0 == $obj->menu_item_parent ) {
// set the key of the parent
$last_top = $key;
} else {
$sorted_menu_items[$last_top]->classes['dropdown'] = 'dropdown';
}
}
return $sorted_menu_items;
}
add_filter( 'wp_nav_menu_objects', 'menu_set_dropdown', 10, 2 );
Alt: intensiv auf der DB
add_filter( 'nav_menu_css_class', 'check_for_submenu', 10, 2);
function check_for_submenu($classes, $item) {
global $wpdb;
$has_children = $wpdb->get_var("SELECT COUNT(meta_id) FROM wp_postmeta WHERE meta_key='_menu_item_menu_item_parent' AND meta_value="".$item->ID.""");
if ($has_children > 0) array_push($classes,'dropdown'); // add the class dropdown to the current list
return $classes;
}
Beachten Sie, dass diese Methode eine weitere Datenbankabfrage für jeden Ihrer Menüpunkte hinzufügt und Ihre Website verlangsamen kann, wenn Sie viele Menüpunkte haben!
– Ahrengot
9. Januar 2013 um 22:36 Uhr
@Ahrengot sehe meinen neuen Weg viel besser für die Datenbank;)
– jaw
10. Januar 2013 um 16:13 Uhr
es hat keine wirkung in wp 3.6? etwas anderes hinzuzufügen oder zu entfernen? ich benutze wp_nav_menu()
– ausschlaggebend
30. September 2013 um 14:36 Uhr
@RashidShafique es funktioniert in 3.6 Hast du ein Menü in der zugewiesen wp-admin? Habe es mit einer sauberen Installation getestet twentyeleven und kein anderes Plugin
– jaw
30. September 2013 um 18:37 Uhr
Tolle Lösung, aber hat jemand eine Idee, warum dies in Unter-Untermenüs nicht funktioniert? (ein Dropdown-Menü in einem Dropdown-Menü)
class Nav_Walker extends Walker_Nav_Menu
{
public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0)
{
if($args->walker->has_children)
{
//code...
}
}
}
Wir haben das Objekt ‘Walker’, das können Sie var_dump($args) um mehr Dinge zu sehen. Ich verwende für mein Projekt!
Willkommen bei SO, Sternennacht und vielen Dank für Ihre erste Antwort. Einige Ratschläge, um eine bessere Punktzahl für Ihre Antworten zu erhalten: Fügen Sie eine Erklärung dazu bei warum Ihr Code wird das Problem des OP lösen.
– nikolaus-hee
29. Juni 2015 um 4:27 Uhr
Jörg Bucaran
Das Problem scheint endlich gelöst zu sein. Die neueste WordPress-Beta ab aktuellem Schreiben 4.0 hat die Walker_Nav_Menu-Klasse aktualisiert und eine hinzugefügt $has_children Eigentum.
/**
* Whether the current element has children or not.
*
* To be used in start_el().
*
* @since 4.0.0
* @access protected
* @var bool
*/
protected $has_children;
Also müssen wir nicht hacken function display_element(...) mehr.
Anstatt zu modifizieren start_elsie modifizierten display_elementindem Sie die folgenden zwei Zeilen hinzufügen (Zeile 37-38 Hier):
//display this element (THESE ARE NOT THE LINES)
if ( is_array( $args[0] ) )
$args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );
// THESE TWO ARE THE LINES:
if( ! empty( $children_elements[$element->$id_field] ) )
array_push($element->classes,'parent');
Ich habe die beiden vorherigen Zeilen als räumliche Referenz und auch als Kommentar zu anderen Antworten in diesem Beitrag hinterlassen. Es scheint, dass WordPress “versucht”, eine ´has_children´-Eigenschaft zu setzen $args, aber es macht es entweder falsch oder auf eine Weise, die ich nicht verstehe. Das jedenfalls has_children Parameter wird niemals weitergegeben start_el (siehe Beispiel var_dump einer $args Hier)
Dies könnte ein Fehler in der WordPress-Version sein, die ich habe (3.2.1), und wurde möglicherweise in der neuesten Version behoben.
Auf jeden Fall ist die Antwort, die ich im WordPress-Forum erhalten habe, diejenige, die mir geholfen hat, das Problem zu beheben, also betrachte ich das als erledigt. Ich werde warten, bis das Kopfgeld abgelaufen ist, falls Keesiemeijer seine Antwort hier posten möchte.
class My_Walker_Nav_Menu extends Walker_Nav_Menu {
function start_el(&$output, $item, $depth, $args) {
...
if($args['has_children']) {
// I know what to do here
}
}
}
Ich fürchte, das wird nicht funktionieren. Aus zwei Gründen – erstens, $args ist ein stdObjectnicht ein array – so wäre die Syntax $args->has_children. Und zweitens, $args hat kein has_children Attribut (Ich habe mit überprüft var_dump($args)). Weder es noch $item scheinen ein solches Attribut zu haben.
– kikito
12. Dezember 2011 um 10:16 Uhr
@kikito könntest du die Ergebnisse von var_dump($args) posten?
– Eugen Manuilov
12. Dezember 2011 um 12:43 Uhr
@kikito seltsam, es hat “has_children” arg an meinem Ende.
– Eugen Manuilow
13. Dezember 2011 um 15:27 Uhr
if ($args->walker->has_children)
– Steven Vachon
9. Februar 2016 um 0:50 Uhr
Gemeinschaft
Kikitos obige Antwort macht den Trick fertig, aber nicht auf die wiederverwendbarste Weise. Meiner Meinung nach ist der bessere Ansatz so:
function display_element($element, &$children_elements, $max_depth, $depth=0, $args, &$output) {
// the first few lines of the method...
//display this element; handle either arrays or objects gracefully
if ( is_array( $args[0] ) )
$args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );
elseif ( is_object($args[0]) )
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
// the rest of the method...
}
Überschreiben Walker::display_element() ist der richtige Schritt, aber es ist aus zwei Gründen besser, das Problem tatsächlich an der Wurzel des Problems anzugehen, als an dieser Stelle einfach eine Klasse anzuhängen. Erstens ist das eigentliche Problem nicht eine fehlende Klasse, sondern der nicht gepatchte Fehler in WordPress, den Kikito bemerkt hat: Das Problem ist das $args[0] ist nicht immer ein Array. Das scheint der typische erwartete Typ für zu sein Walker::display_element()aber eigentlich haben wir es hier mit zu tun Walker_Nav_Menu::display_element()und in diesem Fall args wird als Standardobjekttyp und nicht als Arraytyp übergeben. Als solche müssen wir einfach die hinzufügen has_children -Element, das die Objektnotation anstelle der Array-Notation verwendet. Problem gelöst![1]
Das hinzufügen elseif Konten für den gewöhnlichen Nav-Menü-Fall. Dies ist die gleiche Form, die es hoffentlich in die Kernklasse schaffen wird dieser Patch, dann müssen Sie es nicht mehr verlängern. Sie sollten es wahrscheinlich weiter patchen, um den Fall zu berücksichtigen $args[0] ist weder ein Array noch ein Objekt, aber ich erwarte nicht, dass das passiert.
Zweitens, um eine gute Trennung zwischen den verschiedenen Methoden zu gewährleisten, sollten wirklich Klassen in hinzugefügt werden start_el() Methode oder anderswo, da display_element() tut nichts von der Klassenhandhabung.
Als Ergebnis können Sie dann überschreiben start_el() wie Sie möchten: Sie können Ihre eigenen benutzerdefinierten Klassen hinzufügen oder Elemente vollständig ignorieren oder benutzerdefinierten Text bereitstellen oder was auch immer Sie möchten. (In meinem Fall arbeite ich um eine vorhandene Javascript-Menüimplementierung herum, die sehr spezifische Klassifizierungsanforderungen basierend auf Eltern hat und Kinder, also kann ich nicht einfach die gleichen Klassen zu allem hinzufügen, was ein Kind hat – genau deshalb ist diese Trennung von Bedenken wichtig.) In meinem Code:
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value="";
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
$has_children = (is_object($args) && $args->has_children) || (is_array($args) && $args['has_children']);
if ($has_children) {
// do whatever you need to do
}
// everything else the method does...
}
[1] Dies ist natürlich eine der potenziellen Fallstricke von dynamisch typisierten Sprachen wie PHP … es ist kein Problem, solange Sie vorsichtig sind. Hier haben die WordPress-Entwickler nicht aufgepasst.
Ich fürchte, das wird nicht funktionieren. Aus zwei Gründen – erstens, $args ist ein stdObjectnicht ein array – so wäre die Syntax $args->has_children. Und zweitens, $args hat kein has_children Attribut (Ich habe mit überprüft var_dump($args)). Weder es noch $item scheinen ein solches Attribut zu haben.
– kikito
12. Dezember 2011 um 10:16 Uhr
@kikito könntest du die Ergebnisse von var_dump($args) posten?
– Eugen Manuilow
12. Dezember 2011 um 12:43 Uhr
@kikito seltsam, es hat “has_children” arg an meinem Ende.
– Eugen Manuilow
13. Dezember 2011 um 15:27 Uhr
if ($args->walker->has_children)
– Steven Vachon
9. Februar 2016 um 0:50 Uhr
Zachary Schüssler
Wenn Sie den Aufwand einer harten Abfrage oder Funktion nicht möchten, können Sie dies in jQuery tun:
(function() {
// Add 'has_children' class to menus
jQuery.each(jQuery('.menu-item').has('ul.sub-menu'), function() {
jQuery(this).addClass('has_children');
});
})();
Eine serverseitige PHP-Funktion fügt mit ziemlicher Sicherheit weniger “Overhead” hinzu als ein jQuery-Aufruf am Frontend, insbesondere wenn Sie das Caching berücksichtigen. Darüber hinaus geht diese Lösung (1) davon aus, dass jQuery bereits verwendet wird und (2) dass eine solche Vorgehensweise keine anderen menüorientierten JS im Backend beeinträchtigen würde.
– Chris Krycho
18. April 2013 um 14:42 Uhr
@ChrisKrycho jQuery wird mit WordPress geladen. Das Hinzufügen einer neuen Klasse hat keine Auswirkungen auf das Backend. Auf Servern mit hoher Auslastung sollten Sie eine weitere Abfrage vermeiden. Und natürlich ist dies nur eine Lösung, für die Sie sich entscheiden sollten.
– Zachary Schüssler
18. April 2013 um 14:54 Uhr
Ah, guter Punkt, das hatte ich vergessen (da ich am Ende immer die gelieferte Version ziehe und meine eigene pushe, weil die gelieferte Version fast nie die neueste ist).
– Chris Krycho
18. April 2013 um 15:06 Uhr
8679800cookie-checkWordPress – woher weiß ich, ob ein Menüpunkt Kinder hat?yes