Dynamically switch template on click

My answer is something like an application of one of the @allenericr answer points + some cache.

I suggest to implement this only if the 3 templates are different in the content and not only in the aspect, otherwise a CSS + JS solution should be the best choice.

Create a main page template 'multi-template-page.php' that only contain the switch buttons and a get_template_part call to include one of the 3 different template files (named e.g. my-template-1.php, my-template-2.php and my-template-3.php) that you have to write for every template.

In the main template put something like:

<?php
/*
Template Name: Multi Template Page
*/

get_header(); ?>

<ul id="template_switches">
  <li><a href="#template-1" data-template="1" class="active">Template 1</a></li>
  <li><a href="#template-2" data-template="2">Template 2</a></li>
  <li><a href="#template-2" data-template="3">Template 3</a></li>
</ul>
<div id="template-target"><?php get_template_part('my-template', '1'); ?></div>

<?php get_footer(); ?>

So, by default you’ll have first template active and displayed.

Then, in functions.php enqueue a script for this template:

add_action('wp_enqueue_scripts', 'template_switch_script');

function template_switch_script() {
  if ( is_page_template('multi-template-page.php') ) {
    wp_enqueue_script('multi-template', get_template_directory_uri() . '/js/multi-template.js', array('jquery'));
    $data = array(
       'url' => admin_url('admin-ajax.php'),
       'id' => get_queried_object()->ID
    );
    wp_localize_script('multi-template', 'multiTemplateData', $data );
  }
}

Now you have to create the jsvascript file multi-template.js in js subfolder of your theme:

window.multiTemplateCache = {} // prepare a cache object

jQuery().ready( function($) {

  // cache the 1st template on load
  window.multiTemplateCache.template_1 = $('#template-target').html();

  $('#template_switches li a').click( function(e) {
    e.preventDefault();
    if ( $(this).hasClass('active') ) return false; // do nothing if click active link

    var template = $(this).data('template');

    if ( window.multiTemplateCache['template_' + template ] ) { // look in cache

       // in cache, just output to page
       $('#template-target').html( window.multiTemplateCache['template_' + template] );

    } else { // not in cache, retrieve via ajax

      // remove current template and put a loading message
      $('#template-target').html( 'loading...' ); 

      // css stuff
      $('#template_switches li a').removeClass('active');
      $(this).addClass('active');

      $.get(
        multiTemplateData.url,
        {
          postId: multiTemplateData.id,
          action: 'multi_template_template',
          which: template
        },
        function( data ) {
          // cache the template
          window.multiTemplateCache['template_' + template ] = data;
          // output the template to page
          $('#template-target').html( data );
        }
      );

    } // end if

  }); // end on click event

}); // on ready event

This script on click on one of the switch links, check in a cache object if the template is already loaded, and if so, output it. If not send an ajax request passing the template required.

Before putting the content to page, it is cached, so the ajax call is fired once per template. But only for templates 2 and 3 because template 1 is cached on load 🙂

So, now in your function.php you have to to add the ajax action and the relative function.

add_action('wp_ajax_multi_template_template', 'multi_template_template');
add_action('wp_ajax_nopriv_multi_template_template', 'multi_template_template');

function multi_template_template() {
  $template = isset($_REQUEST['which']) ? $_REQUEST['which'] : false;
  if ( ! $template ) die('Sorry, impossible to load content');
  $postid = isset($_REQUEST['postId']) ? $_REQUEST['postId'] : false;
  if ( $postid ) {
    global $post; // I'm setting global $post just-in-case the template make use of it
    $post = get_post($postid);
  }
  get_template_part('my-template', $template);
  die();
}

You can improve my code, adding the # feature, i.e. when you point to an address like http://example.com/multi-template-page/#template2 the template 2 is setted as active and loaded for first…