1

I am currently developing a one-page based layout with WordPress where the actual content is split up into separate pages in the backend, which are all knit together to a one-page layout in my front-page template file.

The WordPress menus allow for custom URLs which is probably the easiest way to cater a one-pager by referring to #hash-based links which lead to the corresponding divs.

However, I am looking for the probably cleanest solution to organise the menu and its anchors so that the actual editors/admins of a site can simply link to an actual page and have a custom walker translate that into a hash that leads to the right section on the frontend.

So far I would go with the slug of the single pages, let the wp_query pick up the slug and add it as an ID to the wrapping div, e.g.

Page 1 called 'The about page' has a slug 'about-page', so the div then looks like

<div class="section_wrapper" id="about-page"></div>

and type that slug as the hash URL in the menu editor.

Is there a simple walker or another solution that helps out in this case, i.e. turns links to pages into simple hashes? Or an even better practice?

2 Answers 2

2

Well, after some more intense research I finally found a viable solution that is clean and simple and works flawless so far.

Just put this into your functions.php to create a custom walker which turns classic permalinks into hashes (e.g. page.com/mypage to page.com/#mypage):

/* Custom nav walker */

class Single_Page_Walker extends Walker_Nav_Menu{
    function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
       global $wp_query;
       $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
       $class_names = $value = '';
       $classes = empty( $item->classes ) ? array() : (array) $item->classes;
       $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
       $class_names = ' class="'. esc_attr( $class_names ) . '"';
       $output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';
       $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
       $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
       $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
       if($item->object == 'page')
       {
            $varpost = get_post($item->object_id);
            if(is_home()){
              $attributes .= ' href="#' . $varpost->post_name . '"';
            }else{
              $attributes .= ' href="'.home_url().'/#' . $varpost->post_name . '"';
            }
       }
       else
            $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';
        $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID );
        $item_output .= $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;
        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args, $id );
    }
}

Then, where you want your menu to appear, put the following code to call your menu (which you should have defined as a menu position before in your functions.php), and adjust the options to your liking, apart from the 'walker' part:

<?php 
    wp_nav_menu(array(
    'theme_location' => 'onepage',
    'echo' => true,
    'container' => false,
    'walker'=> new Single_Page_Walker,
    'depth' => 1) );
?>

— that’s it already. Now you can fill up your menu in the backend without caring about hashes anymore :).

0

I'm currently doing this on a project.

First, I use the slug to create the anchor on the onepage-template:

<article id="post-<?= get_slug(get_the_ID()); ?>" class="container">

Then. I use the following code in a custom menu walker class to replace the url in the href attribute of the menu item.

/**
* Figure out if the current item is on the page - replace anchor
*/
if (is_page_template('template-forside.php')) {
    $onepageID = get_the_ID();
} else {
    $onepageID = get_option('page_on_front');
}


// Build array of IDs that are on the page
$onepageids = array($onepageID);
$pages = get_pages('child_of='.$onepageID);
foreach($pages as $child) {
    array_push($onepageids, $child->ID);
}

if( in_array($item->object_id,$onepageids) ) {
    if ( is_page_template('template-forside.php') ) {
        // The menu item links to the current page
        $atts['href']   = '#post-'.get_slug($item->object_id);
    } else {
        $atts['href'] = get_home_url().'#post-'.get_slug($item->object_id);
    }


} else {
    $atts['href']   = ! empty( $item->url )        ? $item->url        : '';
}

Notice that I do three things:

  1. Figure out if this actually is the one-page-template (template-forside.php) or not. If not, then the front-page of the site is the onepage-page.
  2. Build an array of page-id's that are on the front page
  3. Use that array to check if the menu-objects id is on the one-page template.
  4. If it is, then I either change the href to the slug anchor (if the onepage is the current page). Or add the home url befor the slug anchor (of not on the onepage as of now)

NOTE - this code is kind of flawed because it assumes that the one-page template is the front page, so it can not completely be generalized.

1
  • This looks good, could you elaborate on how you actually construct the walker? The one you quoted isn’t complete, is it? I actually use only my font-page.php and will utilise index.php for smartly redirecting any sub-pages people might land on to the actual section on my one-pager.
    – physalis
    Jun 15, 2015 at 19:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.