In a previous article we created a photo gallery custom post type in WordPress. I have had numerous requests to create a front end to tie it all together, so lets go!
Overview
A simple theme with some good logic can go a long way in the UI/UX department. The goal of this tutorial is to create a one page website that shows some photography that is filterable and viewable via a lightbox.
Note, that I will be using SASS in this project. Feel free to write the css anyway you please.
Add Another Custom Image Size
1 2 3 4 5 6 7 8 |
//---------------------------------------------- //--------------add theme support for thumbnails //---------------------------------------------- if ( function_exists( 'add_theme_support')){ add_theme_support( 'post-thumbnails' ); } add_image_size( 'admin-list-thumb', 80, 80, true); //admin thumbnail preview add_image_size( 'album-grid', 450, 450, true ); |
You should already have most of the code above from the previous tutorial. We need to add an additional size to create our front-end. On line 8 we add another size called album-grid that will be 450px by 450px. That was simple enough.
Style.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
/* Theme Name: Photo Gallery Theme URI: https://jasonskinner.me Description: Photo Gallery Demo Author: jasonskinner Author URI: https://jasonskinner.me Version: 1.0 */ /*--------------------------------------------------------------------*/ /*----------------------->>>>>>>>>>>>> CSS RESET V2.1 - Jason Skinner*/ /*--------------------------------------------------------------------*/ /* line 17, ../sass/_reset.scss */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; outline: 0; font-size: 100%; font: inherit; vertical-align: baseline; background: transparent; } /* line 28, ../sass/_reset.scss */ body { line-height: 1; } /* line 32, ../sass/_reset.scss */ blockquote, q { quotes: none; } /* line 36, ../sass/_reset.scss */ blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } /* line 41, ../sass/_reset.scss */ ol, ul { list-style: none; } /* line 45, ../sass/_reset.scss */ ins { text-decoration: none; } /* line 49, ../sass/_reset.scss */ del { text-decoration: line-through; } /* tables still need 'cellspacing="0"' */ /* line 54, ../sass/_reset.scss */ table { border-collapse: collapse; border-spacing: 0; } /* Tell the browser to render HTML 5 elements as block */ /* line 60, ../sass/_reset.scss */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } /*Remove vertical textarea scrollbar in IE */ /* line 65, ../sass/_reset.scss */ textarea { overflow: auto; } /*--------------------------------------------------------------------*/ /*-------------------------------------------->>>>>>>>>>>>> Framework*/ /*--------------------------------------------------------------------*/ /* line 7, ../sass/style.scss */ html { overflow-y: scroll; } /* line 9, ../sass/style.scss */ body { font-family: "Ubuntu", sans-serif; } /* line 11, ../sass/style.scss */ header.header { width: 240px; position: fixed; background: #91cccb; left: 0; top: 0; height: 100%; } /* line 19, ../sass/style.scss */ header.header a.logo { display: block; background: url(../../images/logo.png) no-repeat; width: 240px; height: 160px; text-indent: -99999px; } /* line 27, ../sass/style.scss */ header.header .options { float: right; display: block; } /* line 33, ../sass/style.scss */ .content { margin-left: 240px; } /*--------------------------------------------------------------------*/ /*---------------------------------------->>>>>>>>>>>>> Photo Gallery*/ /*--------------------------------------------------------------------*/ /* line 42, ../sass/style.scss */ ul.photogal li { width: 19.92%; float: left; } /* line 45, ../sass/style.scss */ ul.photogal li a { width: 100.1%; display: block; } /* line 49, ../sass/style.scss */ ul.photogal li a img { display: block; max-width: 100.1%; height: auto; width: auto\9; /* ie8 */ } /* line 60, ../sass/style.scss */ ul#filters { overflow: hidden; width: 200px; margin: 20px 20px 0px 20px; } /* line 65, ../sass/style.scss */ ul#filters li { float: left; margin: 0 0 15px 0; } /* line 69, ../sass/style.scss */ ul#filters li a { display: block; background: #fff; padding: 8px 10px; width: 178px; text-decoration: none; border-right: 2px solid #79aeac; border-bottom: 2px solid #79aeac; color: #f87242; } /* line 79, ../sass/style.scss */ ul#filters li a:hover { background: #f7f5f2; border-right: 2px solid #82bcba; border-bottom: 2px solid #82bcba; } /*--------------------------------------------------------------------*/ /*------------------------------------>>>>>>>>>>>>> Isotope Animation*/ /*--------------------------------------------------------------------*/ /* line 90, ../sass/style.scss */ .isotope, .isotope .isotope-item { -webkit-transition-duration: 0.8s; -moz-transition-duration: 0.8s; -ms-transition-duration: 0.8s; -o-transition-duration: 0.8s; transition-duration: 0.8s; } /* line 99, ../sass/style.scss */ .isotope { -webkit-transition-property: height, width; -moz-transition-property: height, width; -ms-transition-property: height, width; -o-transition-property: height, width; transition-property: height, width; } /* line 107, ../sass/style.scss */ .isotope .isotope-item { -webkit-transition-property: -webkit-transform, opacity; -moz-transition-property: -moz-transform, opacity; -ms-transition-property: -ms-transform, opacity; -o-transition-property: -o-transform, opacity; transition-property: transform, opacity; } /* line 119, ../sass/style.scss */ .isotope.no-transition, .isotope.no-transition .isotope-item, .isotope .isotope-item.no-transition { -webkit-transition-duration: 0s; -moz-transition-duration: 0s; -ms-transition-duration: 0s; -o-transition-duration: 0s; transition-duration: 0s; } |
We need to create the default style.css to let WordPress know some information about the theme we are creating. I’ve copied the compiled SASS above, but feel free to remove the generate comments and to format the CSS above however you want.
Notice how on line 117, we do some crazy percentages. Let me explain why we did that. Browsers can usually support CSS percentages up to the fourth decimal place (ie – 33.3333%), but Javascript rounds off the fractional pixels, which can cause the last photo to sometimes drop to the next line when Isotope does the math which leaves a gap. So, we make the list item smaller to give Isotope plenty of room to fit it in where it belongs no matter the browser window width. We counter this by making the anchor link and image slightly bigger, thus making it look perfect.
Header.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<!doctype html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <!--Dynamic Page Titles--> <title><?php bloginfo('name'); ?><?php wp_title(' - ', true, 'left'); ?></title> <!--Call Stylesheets--> <link rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>" type="text/css" /> <link rel="stylesheet" href="<?php bloginfo('template_directory'); ?>/stylesheets/compiled/style.css" type="text/css" /> <link rel="stylesheet" href="<?php bloginfo('template_directory'); ?>/stylesheets/compiled/jquery.fancybox.css" type="text/css" /> <!--Google Font API--> <link href='http://fonts.googleapis.com/css?family=Ubuntu' rel='stylesheet' type='text/css'> <!--wp_head hook to pull in enqueued scripts--> <?php wp_head(); ?> </head> <body> <header class="header"> <a class="logo" href="<?php bloginfo('url'); ?>"><?php bloginfo('title'); ?></a> <div id="options"> <?php //check to see if our custom tag cloud exists and display it if( function_exists( 'jss_tag_cloud' )) { jss_tag_cloud( $args = '' ); } else { //funny error message. probably need to replace this in your build. echo 'Something has gone terribly wrong here!'; } ?> </div> </header><!--end header--> |
Next, lets create the header.php file that we can apply to our template later. Notice, we are using HTML5, but you don’t have to. In our head we are calling some stylesheets, a Google font API and our wp_head.
In the header, we are including the logo and a options div that will display our custom tag cloud, if it exists, that we will build in just a bit.
Footer.php
1 2 3 |
<?php wp_footer(); ?> </body> </html> |
The footer.php is simple.
Functions.php
The functions file is a little more complicated, so let’s break it up into a couple of different sections. I will run though it, so you can possibly copy and paste each section and it will work.
To be honest, I would really break this up into multiple files on a real project and include all of the different parts in a lib file within our theme and then branch them in to segments (inits, core, gallery, extensions, etc). Yet, for the sake of time, let’s put it all together with the code from the previous tutorial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//---------------------------------------------- //------------------------------------enqueue js //---------------------------------------------- function jss_load_scripts(){ //deregister for google jQuery cdn wp_deregister_script( 'jquery' ); wp_register_script( 'jquery', "http" . ( $_SERVER['SERVER_PORT'] == 443 ? "s" : "" ) . "://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js", array(), false, true ); wp_enqueue_script( 'jquery' ); //register fancybox. change this to your local file. wp_deregister_script( 'fancybox' ); wp_register_script( 'fancybox', "http" . ( $_SERVER['SERVER_PORT'] == 443 ? "s" : "" ) . "://cdnjs.cloudflare.com/ajax/libs/fancybox/2.1.4/jquery.fancybox.pack.js", array(), false, true ); wp_enqueue_script( 'fancybox' ); //register modernizr. change this to your local file. wp_deregister_script( 'modernizr' ); wp_register_script( 'modernizr', "http" . ( $_SERVER['SERVER_PORT'] == 443 ? "s" : "" ) . "://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js", array(), false, true ); wp_enqueue_script( 'modernizr' ); //register isotope. change this to your local file. wp_deregister_script( 'isotope' ); wp_register_script( 'isotope', "http" . ( $_SERVER['SERVER_PORT'] == 443 ? "s" : "" ) . "://cdnjs.cloudflare.com/ajax/libs/jquery.isotope/1.5.25/jquery.isotope.min.js", array(), false, true ); wp_enqueue_script( 'isotope' ); //general wp_register_script( 'general', get_template_directory_uri() . '/js/general.js', array(), false, true ); wp_enqueue_script( 'general' ); } //set it off add_action( 'wp_enqueue_scripts', 'jss_load_scripts' ); |
In this function, we are registering all of our javascript files the right way within wordpress. You could just include them in the header.php file, but WordPress has no idea of what you are doing. By enqueuing the files, we can set dependencies and place them in the header or the footer.
We are using Modernizr because we are using HTML5, so if you are not using it you can leave it out.
I am loading most of the scripts from a Content Delivery Network, so you can keep it that way or go download the files and make them local. I would suggest you keep using jQuery from the Google CDN because it reduces bandwidth usage, is minified and increases the chance that the user has already cached the file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
//---------------------------------------------- //-------------------custom tag cloud generation //---------------------------------------------- function jss_generate_tag_cloud( $tags, $args = '' ) { global $wp_rewrite; //don't touch these defaults or the sky will fall $defaults = array( 'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'number' => 0, 'format' => 'flat', 'separator' => "\n", 'orderby' => 'name', 'order' => 'ASC', 'topic_count_text_callback' => 'default_topic_count_text', 'topic_count_scale_callback' => 'default_topic_count_scale', 'filter' => 1 ); //determine if the variable is null if ( !isset( $args['topic_count_text_callback'] ) && isset( $args['single_text'] ) && isset( $args['multiple_text'] ) ) { //var_export $body = 'return sprintf ( _n(' . var_export($args['single_text'], true) . ', ' . var_export($args['multiple_text'], true) . ', $count), number_format_i18n( $count ));'; //create_function $args['topic_count_text_callback'] = create_function('$count', $body); } //parse arguments from above $args = wp_parse_args( $args, $defaults ); //extract extract( $args ); //check to see if they are empty and stop if ( empty( $tags ) ) return; //apply the sort filter $tags_sorted = apply_filters( 'tag_cloud_sort', $tags, $args ); //check to see if the tags have been pre-sorted if ( $tags_sorted != $tags ) { // the tags have been sorted by a plugin $tags = $tags_sorted; unset($tags_sorted); } else { if ( 'RAND' == $order ) { shuffle($tags); } else { // SQL cannot save you if ( 'name' == $orderby ) uasort( $tags, create_function('$a, $b', 'return strnatcasecmp($a->name, $b->name);') ); else uasort( $tags, create_function('$a, $b', 'return ($a->count > $b->count);') ); if ( 'DESC' == $order ) $tags = array_reverse( $tags, true ); } } //check number and slice array if ( $number > 0 ) $tags = array_slice($tags, 0, $number); //set array $counts = array(); //set array for alt tag $real_counts = array(); foreach ( (array) $tags as $key => $tag ) { $real_counts[ $key ] = $tag->count; $counts[ $key ] = $topic_count_scale_callback($tag->count); } //determine min coutn $min_count = min( $counts ); //default wordpress sizing $spread = max( $counts ) - $min_count; if ( $spread <= 0 ) $spread = 1; $font_spread = $largest - $smallest; if ( $font_spread < 0 ) $font_spread = 1; $font_step = $font_spread / $spread; $a = array(); //iterate thought the array foreach ( $tags as $key => $tag ) { $count = $counts[ $key ]; $real_count = $real_counts[ $key ]; $tag_link = '#' != $tag->link ? esc_url( $tag->link ) : '#'; $tag_id = isset($tags[ $key ]->id) ? $tags[ $key ]->id : $key; $tag_name = $tags[ $key ]->name; //If you want to do some custom stuff, do it here like we did //call_user_func $a[] = "<a href='#filter' class='tag-link-$tag_id' data-option-value='.$tag_name' title='" . esc_attr( call_user_func( $topic_count_text_callback, $real_count ) ) . "'>$tag_name</a>"; //background-color is added for validation purposes. } //set new format switch ( $format ) : case 'array' : $return =& $a; break; case 'list' : //create our own setup of how it will display and add all $return = "<ul id='filters' class='option-set' data-option-key='filter'>\n\t <li><a href='filter' data-option-value='*' class='selected'>All</a></li> <li>"; //join $return .= join( "</li>\n\t<li>", $a ); $return .= "</li>\n</ul>\n"; break; default : //return $return = join( $separator, $a ); break; endswitch; //create new filter hook so we can do this return apply_filters( 'jss_generate_tag_cloud', $return, $tags, $args ); } //---------------------------------------------- //---------------------custom tag cloud function //---------------------------------------------- //the function below is very similar to 'wp_tag_cloud()' currently located in: 'wp-includes/category-template.php' function jss_tag_cloud( $args = '' ) { //set some default $defaults = array( 'format' => 'list', //display as list 'taxonomy' => 'phototype', //our custom post type taxonomy 'hide_empty' => 'true', 'echo' => true, //touch this and it all blows up 'link' => 'view' ); //use wp_parse to merge the argus and default values $args = wp_parse_args( $args, $defaults ); //go fetch the terms of our custom taxonomy. query by descending and order by most posts $tags = get_terms( $args['taxonomy'], array_merge( $args, array( 'orderby' => 'count', 'order' => 'DESC' ) ) ); //if there are no tags then end function if ( empty( $tags )) return; //set the minimum number of posts the tag must have to display (change to whatever) $min_num = 1; //logic to display tag or not based on post count foreach($tags as $key => $tag) { //if the post container lest than the min_num variable set above if($tag->count < $min_num) { //unset it and destroy part of the array unset($tags[$key]); } } foreach ( $tags as $key => $tag ) { if ( 'edit' == $args['link'] ) //display the link to edit the tag, if the user is logged in and has rights $link = get_edit_tag_link( $tag -> term_id, $args['taxonomy'] ); else //get the permalink for the taxonomy $link = get_term_link( intval($tag -> term_id), $args['taxonomy'] ); //check if there is an error if ( is_wp_error( $link ) ) return false; $tags[ $key ] -> link = $link; $tags[ $key ] -> id = $tag -> term_id; } //generate our tag cloud $return = jss_generate_tag_cloud( $tags, $args ); // here is where whe list what we are sorting //create a new filter hook $return = apply_filters( 'jss_tag_cloud', $return, $args ); if ( 'array' == $args['format'] || empty($args['echo']) ) return $return; echo $return; } //Hooks a function to a specific filter action. //hook function to filter add_filter('wp_tag_cloud', 'jss_tag_cloud'); |
I know this looks like a ton of code, but it really isn’t. We are basically cloning the core WordPress wp_tag_cloud and wp_generate_tag_cloud functions so we can make some tweaks in how they are generated and displayed. We also allow the core tag cloud the ability to be used for the blog or any other part of the site.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//---------------------------------------------- //-------------------------get CPT taxonomy name //---------------------------------------------- function jss_taxonomy_name(){ global $post; //get terms for CPT $terms = get_the_terms( $post->ID , 'phototype' ); //iterate through array foreach ( $terms as $termphoto ) { //echo taxonomy name as class echo ' '.$termphoto->name; } } |
The above function simply goes to our custom post type of gallery and grabs the phototype and displays it. We can use this inside the loop and will also keep our HTML cleaner.
General.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
//put jQuery in noConflict mode var $j = jQuery.noConflict(); $j(document).ready(function() { //---------------------------------------------- //--------------------------------------fancybox //---------------------------------------------- //simple fancybox start $j('.fancybox').fancybox({ //make sure fancybox knows we are loading images from wordpress 'type': 'image', 'autoSize' : true, //lock the background when fancybox is active so weird padding doesn't show up helpers : { overlay : { locked : false } } }); //---------------------------------------------- //---------------------------------------isotope //---------------------------------------------- //set container variable so we don't have to type alot var $container = $j('.photogal'); //run function when all images touched by isotope are loaded $container.imagesLoaded( function(){ //set parameters $container.isotope({ //tell isotope what to target itemSelector : '.element', //set the layout mode layoutMode: 'fitRows', //tell isotope to use CSS3 if it can and fallback to jQuery animationEngine : 'best-available', //set masonry parameter masonry: { //we want 5 columns columnWidth: $container.width() / 5 } }); }); //tell isotope our filters are in the options id & links var $optionSets = $j('#options'), $optionLinks = $optionSets.find('a'); //click function to sort by data $optionLinks.click(function(){ var $this = $j(this); // don't proceed if already selected if ( $this.hasClass('selected') ) { return false; } var $optionSet = $this.parents('.option-set'); $optionSet.find('.selected').removeClass('selected'); $this.addClass('selected'); // make option object dynamically, i.e. { filter: '.my-filter-class' } var options = {}, key = $optionSet.attr('data-option-key'), value = $this.attr('data-option-value'); // parse 'false' as false boolean value = value === 'false' ? false : value; options[ key ] = value; if ( key === 'layoutMode' && typeof changeLayoutMode === 'function' ) { // changes in layout modes need extra logic changeLayoutMode( $this, options ); } else { // otherwise, apply new options $container.isotope( options ); } return false; }); }); |
In the jQuery above, the first thing we do is to activate noConflict mode. I always do this when working with WordPress because it keeps our javascript from conflicting with any javascript Worpress needs.
Next we run Fancybox and make sure it knows that we are using images (Fancybox absolutely throws a fit if it doesn’t have an explicit documentType with WordPress) and we also need the overlay helper to lock the background when Fancybox is initiated. I found that there was a weird spacing bug when dealing with Isotope. Remove the helper and see for yourself. You can read more about Fancybox and its API here.
Next we need to tell Isotope what we want it to do, because it can do a lot of cool things. On line 26, we set a container variable to save us some keystrokes. Line 28 is when we initiate Isotope and tell it want we want it to watch for (itemSelector), what layoutMode we want, which animationEngine to use (best-available checks to see if the CSS3 transitions can be used and if not fall back to jQuery) and finally how many columns we want.
Next we need to tell Isotope what we want to filter. On line 46, we tell Isotope to find the anchor links insite of the options div and add a click function on line 50 to actually initiate the filtering. You can see more examples of Isotope and how to use it here.
Index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?php get_header(); ?> <div class="content"> <ul class="photogal"> <?php //setup new WP_Query $wp_query = new WP_Query( array( 'posts_per_page' => -1, 'post_type' => 'gallery' ) ); //begine loop while ($wp_query->have_posts()) : $wp_query->the_post(); ?> <li class="element <?php if( function_exists('jss_taxonomy_name')){ jss_taxonomy_name(); }?>"> <a class="fancybox" rel="<?php if( function_exists('jss_taxonomy_name')){ jss_taxonomy_name(); }?>" href=" <?php //get post thumbnail id $image_id = get_post_thumbnail_id(); //go get image attributes [0] => url, [1] => width, [2] => height $image_url = wp_get_attachment_image_src($image_id,'', true); //echo out the url echo $image_url[0]; ?> "> <?php the_post_thumbnail('album-grid'); //display custom thumbnail size ?> </a> </li><!--end li--> <?php endwhile; // end of the loop. ?> </ul><!--end photogallery--> </div><!--end content--> <?php get_footer(); ?> |
Finally! Let’s get down to writing the WordPress loop. First we get the header.php file we made earlier. Then we start our WP_Query and loop. Notice we do not query_posts! Query_posts should never be used and I see it used more and more on the intranet. Don’t do it! Okay, I’ll step off my soap box now.
Wrapping It Up
I’ve commented the code pretty well and tried to talk about the important steps in the process. Let me know if you have any questions or problems and I will do my best to solve them.
Be sure to include the Fancybox images in your theme.
Feel free to download the zip file that contains the whole WordPress Photo Gallery project to see the whole thing together. It also includes the SASS config and stylesheets, as well as the images.
Now, I am off to grab a smooth glass of Yamazaki whiskey.
Oh my goodness! what a tutorial this is. I have learnt a lot from this thanks very much
Not a problem. I’m glad you found it useful.
Not quite sure if the front-end is what I had in mind 🙁 Is there anyway I can just display each gallery’s featured image on a page instead of a filter…?
You sure can. Just use the index.php as a guide. You really don’t need anything else in the tutorial.
That being said, just use the WP_Query and loop. Inside that loop just use the_post_thumbnail to call the thumbnail for each post and whatever else info you want to display, then close the loop with endwhile.
Let me know if you have any problems. I would be glad to setup an example if needed.
Thanks! I was hoping to edit my question before you replied lol. My new* question was//
Is there anyway to filter each Photo Gallery instead of each individual photo…? Does that make sense? 😡
Btw you respond pretty quickly! That’s awesome! I usually get a response weeks or months later!
Sorry for the late response! I was out of town.
I’m not sure exactly what you are referring to. The ‘photo type’ taxonomy we have created is pretty much a category. Are you talking about a subcategory or multiple photo galleries? My brain is still on vacation mode.
In your demo to me it seems as if every photo from each category is displayed on one page. I was hoping there would be a way to display EACH WHOLE gallery instead…
I’m trying my best to explain lol.
*sigh*
Sorry, I have been speaking developer for so long that sometimes it’s hard for me to understand. 🙂
And let me apologize for the late response, as I have been moving to a new city the past month.
There are no categories in the demo, yet there are custom taxonomies labeled photo types we reference to the custom post type of gallery.
Are you saying you want each photo type on separate pages?
Perfect! This was exactly what I was looking for. One question. To view archives and categories, would you just create category-gallery.php and arhcives-gallery.php? Thanks!
Glad you found it helpful! Sorry for the slow response. I just got done moving to another city (I am writing a post about that right now). To answer your question, you would need to create a archive-gallery.php and instead of using category-gallery.php. You will probably want to change the key to the posts_per_page, so it doesn’t pull everything. That would make an archive useless. Be mindful that if you are wanting to archive any of the custom taxonomies (photo types) you should use taxonomy.php. You can find out more about how WordPress handles that in the codex: http://codex.wordpress.org/Template_Hierarchy. Let… Read more »
No worries. I understand busy. Anyhow, I did manage to get it sorted out. You’re correct, I created the archive-gallery.php and also added ‘has_archive’ => true, into the _args = array( and I was good to go. Nice tutorial and thanks again!
This is great, thanks. Any chance you go explain more in depth (just slightly more…) how you would break out the functions.php file?
A majority of the time, I only keep global functions and settings in my functions.php. I create a ‘lib’ folder within my theme. Inside of the ‘lib’ folder, I would create additional folders that break up the functions (this is really personal preference) by name or type. Then I include that file in the main functions.php. The goal is to end up with a short functions.php file and not a really long one. For example, if I making this custom post type, I would not put it in the functions.php file. I would create the ‘lib’ folder in my theme… Read more »
Superb!! Your tutorial helped me a lot. Thank you very much for this. You’ve explain everything in really good way.
Thanks a lot for this tutorial. I’m not planning to create a gallery but this cleared up a lot of confusion I had regarding custom post type. Great job!
Thanks! It is always good to hear that the tutorial was useful in more than one way.
can you please share the source file?
I usually do not put source files in any of the tutorials I write around the web. I find that people have a tendency to copy and paste, instead of learning the concept. I may need to start including them, if enough people ask.
Well, I’ve gone through this for hours now and while 95% of it is looking awesome, I cannot for the life of me get the featured images to display as the 450 x 450 squares we’ve declared. I also notice that when click a thumbnail image and it comes up, I don’t have the little arrow buttons indicating that the user can click to go to prev/next image. Any words of advice? I’m about to down a bottle of Yamazaki at this point…
I would check to make sure your images are originally larger than 450px X 450px and that you have ‘add_image_size’ in your functions file for ‘album-grid’ (code at the top of this page). Then i would make sure you are calling it correctly in ‘the_post_thumbnail’ in your index.php. If both of those are correct, I would regenerate your images using the Regenerate Thumbnails plugin (http://wordpress.org/plugins/regenerate-thumbnails/). If you had the images already uploaded or changed the size at anytime of ‘alum-grid’, WordPress will not go correct those images. Dealing with the missing images for Fancybox, I would make sure that you… Read more »
That plug-in worked like a charm. Thank you very much sir, you’re a champion.
Now I can have that Yamazaki for the right reasons. Success!
I’m glad you got it sorted out. I may go enjoy some myself!
Hi Jason! Thats a great tutorial! Thanks for that! I’m new with jscript so I cant make it work properly. Actually, i’ve made some changes (not a custom post, but normal posts), and I’m building a image menu filter by hand. BUT, just the “all” button is working. The others buttons are not filtering. Just a bit of code menu filter code (first button): All ‘category’, ‘term_args’ => array( ‘slug’ => ‘webdesign’,)) ); foreach( (array) $terms as $term) { echo wp_get_attachment_image( $term->image_id, ” );}?> -1, ‘post_type’ => ‘post’)); //begine loop while ($wp_query->have_posts()) : $wp_query->the_post();?> <li class="element “> <a rel="” href=””>… Read more »
Hi Jason! Thats a great tutorial! Thanks for that! I’m new with jscript so I cant make it work properly. Actually, i’ve made some changes (not a custom post, but normal posts), and I’m building a image menu filter by hand. BUT, just the “all” button is working. The others buttons are not filtering. Just a bit of code menu filter code (first button): [code] All ‘category’, ‘term_args’ => array( ‘slug’ => ‘webdesign’,)) ); foreach( (array) $terms as $term) { echo wp_get_attachment_image( $term->image_id, ” );}?> -1, ‘post_type’ => ‘post’)); //begine loop while ($wp_query->have_posts()) : $wp_query->the_post();?> <li class="element “> <a rel="”… Read more »
Hi Jason! Thats a great tutorial! Thanks for that! I’m new with jscript so I cant make it work properly. Actually, i’ve made some changes (not a custom post, but normal posts), and I’m building a image menu filter by hand. BUT, just the “all” button is working. The others buttons are not filtering. Just a bit of code menu filter code (first button) – I removed the ?(php) to post: All [image] $wp_query = new WP_Query( array(‘posts_per_page’ => -1, ‘post_type’ => ‘post’)); //begine loop while ($wp_query->have_posts()) : $wp_query->the_post();?> <li class="element “> <a rel="” href=””> —– I transported all the… Read more »
Hi Jason! Thats a great tutorial! Thanks for that! I’m new with jscript so I cant make it work properly. Actually, i’ve made some changes (not a custom post, but normal posts), and I’m building a image menu filter by hand. BUT, just the “all” button is working. The others buttons are not filtering. ul id=”filters” class=”option-set” data-option-key=”filter” a href=’filter’ data-option-value=’*’ class=’selected’>All array(‘posts_per_page’ => -1, ‘post_type’ => ‘post’)); <li class="element “> <a rel="” —– I transported all the options from original menu, but the filters aren’t working. var $optionSets = $j(‘#mydiv’), and on functions.php: //get terms for CPT $terms =… Read more »
Sorry for the late reply! Is there anyway you could show me your code in a gist or codepen? I’ll try to look over it.
https://gist.github.com/ or http://codepen.io/pen/
I think I know why this lad is having trouble filtering… first off if you create a new category in your taxamony (sorry for spelling im french btw) this example shows off (Landscape, portrait, wedding)…. now thats all fine and dandy but, try renaming one “Mikes wedding” instead… right off the bat it should no longer filter correctly and I believe this is why : If you inspect the options list you will see as follow : Mikes wedding (at first I thought this was a general.js error but its not) data-option-value=”.Mikes wedding” <— notice the white space between the… Read more »
I think I know why this lad is having trouble filtering… first off if you create a new category in your taxamony (sorry for spelling im french btw) this example shows off (Landscape, portrait, wedding)…. now thats all fine and dandy but, try renaming one “Mikes wedding” instead… right off the bat it should no longer filter correctly and I believe this is why : If you inspect the options list you will see as follow : a href=”#filter” class=”tag-link-15″ data-option-value=”.Mikes wedding” title=”1″>Mikes wedding</a (at first I thought this was a general.js error but its not) data-option-value=".Mikes wedding" a class=”fancybox”… Read more »
I think I know why this lad is having trouble filtering… first off if you create a new category in your taxamony (sorry for spelling im french btw) this example shows off (Landscape, portrait, wedding)…. now thats all fine and dandy but, try renaming one “Mikes wedding” instead… right off the bat it should no longer filter correctly and I believe this is why : If you inspect the options list you will see as follow : a href=”#filter” class=”tag-link-15″ data-option-value=”.Mikes wedding” title=”1″>Mikes wedding</a (at first I thought this was a general.js error but its not) data-option-value=".Mikes wedding" a class=”fancybox”… Read more »
I think I know why this lad is having trouble filtering… first off if you create a new category in your taxamony (sorry for spelling im french btw) this example shows off (Landscape, portrait, wedding)…. now thats all fine and dandy but, try renaming one “Mikes wedding” instead… right off the bat it should no longer filter correctly and I believe this is why : If you inspect the options list you will see as follow : data-option-value=”.Mikes wedding” (at first I thought this was a general.js error but its not) data-option-value=”.Mikes wedding” a class=”fancybox” rel=” Mikes Wedding” href=”1224915271.jpg “>… Read more »
I think I know why this lad is having trouble filtering… first off if you create a new category in your taxamony (sorry for spelling im french btw) this example shows off (Landscape, portrait, wedding)…. now thats all fine and dandy but, try renaming one “Mikes wedding” instead… right off the bat it should no longer filter correctly and I believe this is why : If you inspect the options list you will see as follow : data-option-value=”.Mikes wedding” (at first I thought this was a general.js error but its not) data-option-value=”.Mikes wedding” $tag ) { $count = $counts[ $key… Read more »
Thanks Marc! I’ll give it a look over and credit you with anything I find.
Awesome! this helped so much in making a custom slider utilizing the Foundation and Orbit, Cheers!
Glad it helped!
I suggest to try something simple like: http://goo.gl/sQ6yXj it has been wonderful
Thanks for this awesome tutorial!
I would ultimately like each gallery item to have a it’s own page with the title, the image, sharing buttons and a comments box etc. How would I make it so I have a single-gallery-item-template.php file?
Also is there an easy way to include the photo-type into the url as well so…http://www.mywebsite.com/gallery/photo-type/the-mage
Thanks again for the great tutorial!
Nice result but why do you load jQuery and fancy box when you could use thickbox which is already included in WordPress like jQuery is?
I always unregister WP’s version of jQuery because I do not want jQuery updated automatically when I update WordPress. Have you every had jQuery updated to a new alpha release and things go wonky? Because it happens. If I stay with a stable release, then it is guaranteed to work. And if I need the new library, I just change that line of code.
Also, Thickbox is clunky. If you notice with the 3.5 they do not use it anymore. Plus, if I remember correctly, thickbox did not play nicely with the then beta version of isotope’s library.
Thanks for the questions!
Amazing tutorial. By the way how to make isotope filter to certain category by opened link to new tab?
Great tutorial! I have a question regarding the lightbox navigation. If I click on a image and go to previous/next, it only loops the images in one category, For example: Caterogry A (Images 1, 2, 5, 7, 9), Category B (Images 3, 4, 6, 8, 10)…. If all the images are displayed on the page, the lightbox navigation will cycle images that belong to the category of the first selected image rather than the physical arragement of photos on a page. That is, if i click on the image no. 3, the next would be image no. 4, and the… Read more »
I am not able to mege code(header, footer,index,function.php and general.js file)
can any one help the step by step merge Code for “Creating a Filterable WordPress Photo Gallery Using Isotope”
Thanks
Are you referring to having problems merging in Git? I use Git Flow on all my projects. Here is a good cheat sheet I show anyone wanting to know more: https://danielkummer.github.io/git-flow-cheatsheet/