Usando un Walker personalizado, el método start_el()
tiene acceso a $depth
param: cuando es 0
, el elemnt es uno de los mejores, y podemos usar esta información para mantener un contador interno.
Cuando el contador alcanza un límite, podemos usar DOMDocument
para obtener de la salida HTML completa solo el último elemento agregado, envolverlo en un submenú y agregarlo nuevamente a HTML.
Editar
Cuando el número de elementos es exactamente el número que requerimos + 1, por ejemplo. requerimos que 5 elementos sean visibles y el menú tiene 6, no tiene sentido dividir el menú, ya que los elementos serán 6 de cualquier manera.
El código fue editado para abordar eso.
Aquí está el código:
class SplitMenuWalker extends Walker_Nav_Menu {
private $split_at;
private $button;
private $count = 0;
private $wrappedOutput;
private $replaceTarget;
private $wrapped = false;
private $toSplit = false;
public function __construct($split_at = 5, $button = '<a href="#">…</a>') {
$this->split_at = $split_at;
$this->button = $button;
}
public function walk($elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = parent::walk($elements, $max_depth, reset($args));
return $this->toSplit ? $output.'</ul></li>' : $output;
}
public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$this->count += $depth === 0 ? 1 : 0;
parent::start_el($output, $item, $depth, $args, $id);
if (($this->count === $this->split_at) && ! $this->wrapped) {
// split at number has been reached generate and store wrapped output
$this->wrapped = true;
$this->replaceTarget = $output;
$this->wrappedOutput = $this->wrappedOutput($output);
} elseif(($this->count === $this->split_at + 1) && ! $this->toSplit) {
// split at number has been exceeded, replace regular with wrapped output
$this->toSplit = true;
$output = str_replace($this->replaceTarget, $this->wrappedOutput, $output);
}
}
private function wrappedOutput($output) {
$dom = new DOMDocument;
$dom->loadHTML($output.'</li>');
$lis = $dom->getElementsByTagName('li');
$last = trim(substr($dom->saveHTML($lis->item($lis->length-1)), 0, -5));
// remove last li
$wrappedOutput = substr(trim($output), 0, -1 * strlen($last));
$classes = array(
'menu-item',
'menu-item-type-custom',
'menu-item-object-custom',
'menu-item-has-children',
'menu-item-split-wrapper'
);
// add wrap li element
$wrappedOutput .= '<li class="'.implode(' ', $classes).'">';
// add the "more" link
$wrappedOutput .= $this->button;
// add the last item wrapped in a submenu and return
return $wrappedOutput . '<ul class="sub-menu">'. $last;
}
}
El uso es bastante simple:
// by default make visible 5 elements
wp_nav_menu(array('menu' => 'my_menu', 'walker' => new SplitMenuWalker()));
// let's make visible 2 elements
wp_nav_menu(array('menu' => 'another_menu', 'walker' => new SplitMenuWalker(2)));
// customize the link to click/over to see wrapped items
wp_nav_menu(array(
'menu' => 'another_menu',
'walker' => new SplitMenuWalker(5, '<a href="#">more...</a>')
));