Viewing 13 replies - 1 through 13 (of 13 total)
  • Moderator bcworkz

    (@bcworkz)

    Adjust the template so that the main image is within a container that can be accessed via ID by javascript. Woo templates have a brief note in the header comment on how to override templates. Also adjust the template so the image links do not appear, everything is managed via javascript.

    Each thumbnail img tag needs to have an ‘onclick’ attribute added that sets the inner HTML of the main image container to the HTML needed to show the enlarged image. If the main container’s ID is ‘ql-main-img’, then something like this:
    onclick="function(){document.getElementById('ql-main-img').innerHTML = '<img src="http://example.com/images/larger.jpg" />';}"

    Thread Starter webdev00

    (@webdev00)

    Thank you!

    Although I think I may have bitten off more than I can chew.

    This is the contents of ‘content-product.php’ in the woocommerce folder of my flatshop theme (I’m not certain this is what I should be looking for) :

    <?php
    /**
     * The template for displaying product content within loops
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/content-product.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer).
     * will need to copy the new files to your theme to maintain compatibility. We try to do this.
     * as little as possible, but it does happen. When this occurs the version of the template file will.
     * be bumped and the readme will list any important changes.
     *
     * @see     http://docs.woothemes.com/document/template-structure/
     * @author  WooThemes
     * @package WooCommerce/Templates
     * @version 2.5.0
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
    	exit; // Exit if accessed directly
    }
    
    global $product, $woocommerce_loop, $themify;
    
    // Store loop count we're currently on
    if ( empty( $woocommerce_loop['loop'] ) ) {
    	$woocommerce_loop['loop'] = 0;
    }
    
    // Store column count for displaying the grid
    if ( empty( $woocommerce_loop['columns'] ) ) {
    	$woocommerce_loop['columns'] = apply_filters( 'loop_shop_columns', 4 );
    }
    
    // Ensure visibility
    if ( ! $product || ! $product->is_visible() ) {
    	return;
    }
    
    // Increase loop count
    $woocommerce_loop['loop']++;
    
    // Extra post classes
    $classes = array();
    if ( 0 == ( $woocommerce_loop['loop'] - 1 ) % $woocommerce_loop['columns'] || 1 == $woocommerce_loop['columns'] ) {
    	$classes[] = 'first';
    }
    if ( 0 == $woocommerce_loop['loop'] % $woocommerce_loop['columns'] ) {
    	$classes[] = 'last';
    }
    
    ////////////////////////////////////
    // Themify Specific
    ////////////////////////////////////
    
    $full_width = '';
    if ( 'sidebar-none' == $themify->layout ) {
    	$full_width = 'pagewidth';
    }
    if ( 'list-post' != $themify->post_layout ) {
    	$full_width = '';
    }
    
    ?>
    <li data-product-id="<?php echo get_the_ID(); ?>" <?php post_class( $classes ); ?>>
    
    	<?php do_action( 'woocommerce_before_shop_loop_item' ); ?>
    
    	<?php if ( ! $themify->is_related_loop ) : ?>
    		<div class="clearfix <?php echo $full_width; ?>">
    	<?php endif; ?>
    
    		<?php if( 'yes' != $themify->hide_product_image ) : ?>
    			<?php if( 'yes' != $themify->unlink_product_image ) : ?>
    				<a href="<?php the_permalink(); ?>">
    					<?php
    						/**
    						 * woocommerce_before_shop_loop_item_title hook
    						 *
    						 * @hooked woocommerce_show_product_loop_sale_flash - 10
    						 * @hooked woocommerce_template_loop_product_thumbnail - 10
    						 */
    						do_action( 'woocommerce_before_shop_loop_item_title' );
    					?>
    				</a>
    			<?php else : ?>
    				<?php do_action( 'woocommerce_before_shop_loop_item_title' ); ?>
    			<?php endif; ?>
    		<?php endif; ?>
    
    		<?php if ( ! $themify->is_related_loop ) : ?>
    			<div class="summary entry-summary">
    		<?php endif; ?>
    
    			<?php
    				/**
    				 * woocommerce_shop_loop_item_title hook
    				 *
    				 * @hooked woocommerce_template_loop_product_title - 10
    				 */
    				do_action( 'woocommerce_shop_loop_item_title' );
    			?>
    
    			<?php
    				/**
    				 * woocommerce_after_shop_loop_item_title hook
    				 *
    				 * @hooked woocommerce_template_loop_price - 10
    				 */
    				do_action( 'woocommerce_after_shop_loop_item_title' );
    			?>
    
    			<?php
    				/**
    				 * woocommerce_after_shop_loop_item hook
    				 *
    				 * @hooked woocommerce_template_loop_add_to_cart - 10
    				 */
    				do_action( 'woocommerce_after_shop_loop_item' );
    			?>
    
    			<?php
    			if('' == themify_get('setting-hide_shop_more_info')) {
    				$info_link	= get_permalink();
    				$link_class	= '';
    				if(themify_get('setting-product_archive_lightbox_link') != 'no') {
    					$info_link = add_query_arg( array('post_in_lightbox' => '1'), $info_link );
    					$link_class	.= 'themify-lightbox';
    				}
    			?>
    			<a href="<?php echo $info_link; ?>" class="button outline <?php echo $link_class; ?>">
    				<?php _e('More Info', 'themify'); ?>
    			</a>
    			<?php
    			}
    			?>
    
    	<?php if ( ! $themify->is_related_loop ) : ?>
    			</div>
    			<!-- /.summary -->
    
    		</div><!-- /.fullwidth -->
    	<?php endif; ?>
    
    </li>

    I do not know if this is where eventual js should go. I suppose not. I also have a js folder :
    https://i.imgur.com/tCKAzkN.png

    thanks

    Moderator bcworkz

    (@bcworkz)

    Typically the templates in question are in /wp-plugins/woocommerce/templates/single-product/. Create a /single-product/ folder in your theme’s woocommerce folder and copy the proper template files there.

    The templates output the HTML you’re interested in via sprintf(). You will see the resulting HTML is fed through various filters, so instead of customizing templates, you could achieve the same result with filter hooks. Filter hooks are usually preferable, but the string manipulation required to alter the HTML is inherently fragile code. Altering the sprintf() is probably easier as well.

    Even though we are using JS, it is implemented as an HTML attribute, so it actually goes on the template file or in a filter hook, and not as an external JS file.

    Thread Starter webdev00

    (@webdev00)

    alright!

    looking better : https://i.imgur.com/zDi3V2O.png

    I’m supposing nothing extra would be required in this process if I apply it straight to my child theme rather than my theme?

    I’m going to edit my copied “product-image.php” and “product-thumbnails.php”
    to have the onclick attribute.

    I’ll update to say how it goes.

    EDIT::

    I’m supposing that the gallery file is the file I should add the js in :

    <?php
    /**
     * Single Product Thumbnails
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/single-product/product-thumbnails.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer).
     * will need to copy the new files to your theme to maintain compatibility. We try to do this.
     * as little as possible, but it does happen. When this occurs the version of the template file will.
     * be bumped and the readme will list any important changes.
     *
     * @see 	    http://docs.woothemes.com/document/template-structure/
     * @author 		WooThemes
     * @package 	WooCommerce/Templates
     * @version     2.3.0
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
    	exit; // Exit if accessed directly
    }
    
    global $post, $product, $woocommerce;
    
    $attachment_ids = $product->get_gallery_attachment_ids();
    
    if ( $attachment_ids ) {
    	$loop 		= 0;
    	$columns 	= apply_filters( 'woocommerce_product_thumbnails_columns', 3 );
    	?>
    	<div class="thumbnails <?php echo 'columns-' . $columns; ?>"><?php
    
    		foreach ( $attachment_ids as $attachment_id ) {
    
    			$classes = array( 'zoom' );
    
    			if ( $loop === 0 || $loop % $columns === 0 )
    				$classes[] = 'first';
    
    			if ( ( $loop + 1 ) % $columns === 0 )
    				$classes[] = 'last';
    
    			$image_link = wp_get_attachment_url( $attachment_id );
    
    			if ( ! $image_link )
    				continue;
    
    			$image_title 	= esc_attr( get_the_title( $attachment_id ) );
    			$image_caption 	= esc_attr( get_post_field( 'post_excerpt', $attachment_id ) );
    
    			$image       = wp_get_attachment_image( $attachment_id, apply_filters( 'single_product_small_thumbnail_size', 'shop_thumbnail' ), 0, $attr = array(
    				'title'	=> $image_title,
    				'alt'	=> $image_title
    				) );
    
    			$image_class = esc_attr( implode( ' ', $classes ) );
    
    			echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', sprintf( '<a href="%s" class="%s" title="%s" data-rel="prettyPhoto[product-gallery]">%s</a>', $image_link, $image_class, $image_caption, $image ), $attachment_id, $post->ID, $image_class );
    
    			$loop++;
    		}
    
    	?></div>
    	<?php
    }

    but as you can see, instead of an “img” there’s an “a” contaning some sort of shortcode : <a href="%s" class="%s" title="%s" data-rel="prettyPhoto[product-gallery]">%s</a>

    what do I do?

    Moderator bcworkz

    (@bcworkz)

    Yes, that looks like the right file. [product-gallery] is not a shortcode in this case, though it certainly looks like one! I’m not too sure what a data-rel attribute is supposed to indicate. AFAIK it can be used by jQuery to control how the link works on mobile devices, but “prettyPhoto[product-gallery]” is not any value that I would expect for this.

    It’s moot because you will be wanting to delete the a tag anyway. This is the so called “anchor” tag, the HTML that makes links. The img tag you are looking for is output by the 4th %s placeholder for $image. For starters, replace the entire printf() call with just $image and check the output. I’m fairly sure you will get just the img tag with no link.

    Now we need to figure out how to insert an onclick attribute in the img tag. We could use str_replace() to replace the tag’s final ‘/>’ with ‘onclick=”function(){/*JS code here*/} />’, but it’s a fragile approach. What if the img tag had no closing slash? The replacement would fail. There’s a better way. See above the echo line where $image is assigned? We can add attributes to the final array argument. Change this:

    'title'=> $image_title,
    'alt'	=> $image_title
       ) );

    to this:

    'title'=> $image_title,
    'alt'	=> $image_title,
    'onclick'=> "function(){document.getElementById('ql-main-img').innerHTML = '<img src=\"$single_img\" />';}",
       ) );

    If you used a different ID on the other template, it needs to match here. I used the variable $single_img to represent the img tag for the larger image to place in the container with the ID. It needs a real value assigned to it. I believe the following will do it. It can be placed above the $image assignment and below the $image_caption assignment.

    $single_img = wp_get_attachment_image( $attachment_id, 'shop_single', 0, $attr = array(
       'title' => $image_title,
       'alt'   => $image_title,
       ) );

    Disclaimer: This is all untested. In particular I haven’t written JS seriously in a long time, so there may be some issues. In particular, I forget how to deal with nested quotes. The best I recall is simply alternating double and single works in JS. I’m pretty sure I did the PHP quotes correctly, it’s the resulting JS I’m not so sure about.

    Anyway, this should get you extremely close to what you want, but it may not function correctly right out of the gate.

    Thread Starter webdev00

    (@webdev00)

    Hi!

    Wow I’ve gotta say you’ve been really helpful!!

    I think we’re really scratching the surface now.

    I noticed the main image was a Class rather than an id so after testing your code as-is I replaced the onclick with :
    'onclick'=> "function(){document.getElementsByClassName('attachment-shop_single').innerHTML = '<img src=\"$single_img\" />';}",

    this did not help much however. in both cases the images show an onclick in the chrome dev tools but do not have the “clickable” cursor when hovered and don’t do anything when clicked apart for undeclared errors :
    https://i.imgur.com/d9j6txu.png

    https://i.imgur.com/UnY6o3k.png

    Moderator bcworkz

    (@bcworkz)

    Using the class name will work provided that is the only element on the page using that name, otherwise we will not know which element to set the inner HTML for. The class name function returns an array of elements even though there may only be one element in it, so we need to specify the element to act on:
    document.getElementsByClassName('attachment-shop_single')[0].innerHTML

    If there is another element with this class, you are better off adding an ID to the target element since IDs need to be unique on a page.

    The JS error about unexpected token means there is an opening parenthesis somewhere where it does not belong. I assume the highlighted element was the result of clicking (index):473, yes? I don’t see any such error there, but there is an unexpected angle bracket ‘<‘, I’m thinking that is the error and the error messaging isn’t sophisticated enough to distinguish between the various bracketing tokens. To correct the angle bracket issue, the innerHTML needs to be changed to just '$single_img' (including the single quotes)

    I thought $single_img was an HTML reference, not the entire img tag. I was wrong. The entire tag is even better, I’m happier with the result.

    JS doesn’t really care what cursor is shown to the user, it picks up the click event no matter what the cursor is. Of course we want the cursor to change for a proper user experience. We lost the proper pointer by removing the anchor tag. The easiest way to get it back is with CSS:

    .attachment-shop_thumbnail.size-shop_thumbnail {
      cursor: pointer;
    }

    Thread Starter webdev00

    (@webdev00)

    Ok, here’s where I’m at : I found out I had messed up the architecture of woocommerce in my child theme and anyhow I can’t get the edits to these files to show up from my child theme anymore so I’m editing the raw files for now until I figure that out.

    i’m working with product-thumbnails.php and product-image.php :

    <?php
    /**
     * Single Product Thumbnails
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/single-product/product-thumbnails.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer).
     * will need to copy the new files to your theme to maintain compatibility. We try to do this.
     * as little as possible, but it does happen. When this occurs the version of the template file will.
     * be bumped and the readme will list any important changes.
     *
     * @see 	    http://docs.woothemes.com/document/template-structure/
     * @author 		WooThemes
     * @package 	WooCommerce/Templates
     * @version     2.3.0
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
    	exit; // Exit if accessed directly
    }
    
    global $post, $product, $woocommerce;
    
    $attachment_ids = $product->get_gallery_attachment_ids();
    
    if ( $attachment_ids ) {
    	$loop 		= 0;
    	$columns 	= apply_filters( 'woocommerce_product_thumbnails_columns', 3 );
    	?>
    	<div class="thumbnails <?php echo 'columns-' . $columns; ?>"><?php
    
    		foreach ( $attachment_ids as $attachment_id ) {
    
    			$classes = array( 'zoom' );
    
    			if ( $loop === 0 || $loop % $columns === 0 )
    				$classes[] = 'first';
    
    			if ( ( $loop + 1 ) % $columns === 0 )
    				$classes[] = 'last';
    
    			$image_link = wp_get_attachment_url( $attachment_id );
    
    			if ( ! $image_link )
    				continue;
    
    			$image_title 	= esc_attr( get_the_title( $attachment_id ) );
    			$image_caption 	= esc_attr( get_post_field( 'post_excerpt', $attachment_id ) );
    
    			$single_img = wp_get_attachment_image( $attachment_id, 'shop_single', 0, $attr = array(
    				'title' => $image_title,
    				'alt'   => $image_title,
    			) );
    
    			$image       = wp_get_attachment_image( $attachment_id, apply_filters( 'single_product_small_thumbnail_size', 'shop_thumbnail' ), 0, $attr = array(
    				'title'	=> $image_title,
    				'alt'	=> $image_title,
    				'onclick'=> "function(){document.getElementById('centralimage').innerHTML = '<img src=\"'$single_img'\" />';}",
    			) );
    
    			$image_class = esc_attr( implode( ' ', $classes ) );
    
    			echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', $image, $attachment_id, $post->ID, $image_class );
    
    			$loop++;
    		}
    
    		?></div>
    	<?php
    }

    and :

    <?php
    /**
     * Single Product Image
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/single-product/product-image.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer).
     * will need to copy the new files to your theme to maintain compatibility. We try to do this.
     * as little as possible, but it does happen. When this occurs the version of the template file will.
     * be bumped and the readme will list any important changes.
     *
     * @see 	    http://docs.woothemes.com/document/template-structure/
     * @author 		WooThemes
     * @package 	WooCommerce/Templates
     * @version     2.0.14
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
    	exit; // Exit if accessed directly
    }
    
    global $post, $woocommerce, $product;
    
    ?>
    <div class="images">
    	<?php
    		if ( has_post_thumbnail() ) {
    			$image_caption = get_post( get_post_thumbnail_id() )->post_excerpt;
    			$image_link    = wp_get_attachment_url( get_post_thumbnail_id() );
    			$image         = get_the_post_thumbnail( $post->ID, apply_filters( 'single_product_large_thumbnail_size', 'shop_single' ), array(
    				'title'	=> get_the_title( get_post_thumbnail_id() )
    			) );
    
    			$attachment_count = count( $product->get_gallery_attachment_ids() );
    
    			if ( $attachment_count > 0 ) {
    				$gallery = '[product-gallery]';
    			} else {
    				$gallery = '';
    			}
    
    			echo apply_filters( 'woocommerce_single_product_image_html', sprintf( '<a href="%s" itemprop="image" id="centralimage" class="woocommerce-main-image zoom" title="%s" data-rel="prettyPhoto' . $gallery . '">%s</a>', $image_link, $image_caption, $image ), $post->ID );
    
    		} else {
    
    			echo apply_filters( 'woocommerce_single_product_image_html', sprintf( '<img src="%s" alt="%s" />', wc_placeholder_img_src(), __( 'Placeholder', 'woocommerce' ) ), $post->ID );
    
    		}
    	?>
    
    	<?php do_action( 'woocommerce_product_thumbnails' ); ?>
    </div>

    in this last file I added id="centralimage" and removing the href prevents the image from showing up. I don’t know why but the fact that when you click that zone you’re redirected to the image is annoying.

    as you can see the function shows up although I’m not sure about the syntaxe :
    http://i.imgur.com/swO4C61.png

    and so does the id :
    http://i.imgur.com/001N4ha.png

    I’ve tried all the combos of either src=\"'$single_img'\"or src='$single_img' and document.getElementById('centralimage').innerHTML = or document.getElementById('centralimage')[0].innerHTML =

    but the error continues to show up and I cannot get the central image to change. πŸ™

    thank you again!

    Moderator bcworkz

    (@bcworkz)

    I discovered a few things in testing this on my own site. The first thing is, apparently anonymous functions are not allowed for onclick attributes, you have to call a named function. So we name a function. Normally functions should go in a JS file and that file is enqueued. I took a quick and dirty shortcut and just put the following line on product-image.php:
    echo "<script>function bcwSwapImg( img ){document.getElementById('centralimage').innerHTML = img;}</script>";

    This means we need to alter the onclick argument on product-thumbnails.php:
    'onclick'=> "bcwSwapImg( '$single_img' )",

    I changed the sprintf() in the echo apply_filters() line to this:
    sprintf( '<div class="%s" >%s</div>', $image_class, $image )

    Like you, I added the id="centralimage" to the main image anchor tag. Removing the href from an anchor tag will cause the page to not validate. Eventually you should change the anchor tag to a div tag like I did in the sprintf(). This will cause the related CSS using an ‘a’ selector to fail, so you will need to add a CSS rule to achieve the same effect, one that uses ‘div’ instead of ‘a’ as a selector.

    The CSS I added for thumbnails with ‘div’ instead of ‘a’ is this:

    .woocommerce div.product div.thumbnails div.zoom {
      float: left;
      margin-bottom: 1em;
      margin-right: 3.8%;
      width: 30.75%;
    }

    With these adjustments, the thumbnail image was successfully swapped into the larger image area. I then realized that there is no way to get the original image back since there is no thumbnail for it. I didn’t code this aspect up, but it appears that you need to add code on product-thumbnails.php that outputs the main image thumbnail before (or after) the regular thumbnail loop runs.

    The code to get this thumbnail will look much like that on product-image.php, except instead of ‘shop_single’ being the image size argument for get_the_post_thumbnail, it will be ‘shop_thumbnail’.

    Thread Starter webdev00

    (@webdev00)

    Wow!

    Holy Moly someone should really pin this thread.

    I can see it being useful to a lot of people.

    Thanks man, you’re an angel!

    I also added this bit of CSS :

    #centralimage{
        min-height: 50vh;
    }

    to prevent flashing into place of the elements below the central image during the time it was empty (a millisecond).

    Moderator bcworkz

    (@bcworkz)

    You’re welcome, I’m glad it all worked out!

    Nice idea to deal with flashing. It may be much more than a millisecond, depending on one’s connection, because the browser needs to initially request the larger image from the server. Another possible improvement is to preload the larger images into containers at the bottom of the page, positioned WAY off screen (loaded last, but not visible), so the browser can quickly swap in the images from its buffer instead of requesting them from the server.

    Thread Starter webdev00

    (@webdev00)

    wow!

    I’d love to do that, actually!

    how would that be done?

    Moderator bcworkz

    (@bcworkz)

    There’s a few possibilities. Maybe the easiest is to hook the ‘woocommerce_after_single_product’ action. It’s the last thing in the loop to fire. Any later and things get more complicated.

    The callback basically replicates what the product-thumbnails.php template does, except the image size is ‘shop_single’. Output the img tags inside a unique div class. Then style this div with position: relative; and make the left or right offset some big number to ensure it stays off screen, something like right: 300vw; (move right 3 screen widths).

    You may need to reposition everything below the loop that remains on screen to make up for the space the images would have taken. Or not, I forget what the specific behavior is with this. I’m not that good with CSS. There may be a better way to style the images than what I suggested, the main idea is they are positioned way off screen so as to not be visible, but otherwise are displayed normally so the browser caches the images.

    It’d be a good idea to do a quick proof of concept test to be sure the swapping of cached images has the desired effect before investing a lot of time coding this up. You could just temporarily insert the proper hardcoded img tags on the content-single-product.php file at the very end of the file. It doesn’t matter if the images remain visible for this quick test.

Viewing 13 replies - 1 through 13 (of 13 total)
  • The topic ‘Instead of lightbox set main picture to clicked picture in gallery list’ is closed to new replies.