{"id":1945830,"date":"2020-02-13T10:03:09","date_gmt":"2020-02-13T15:03:09","guid":{"rendered":"https:\/\/theeventscalendar.com\/knowledgebase\/?post_type=tribe-knowledgebase&#038;p=1945830"},"modified":"2026-04-02T17:33:28","modified_gmt":"2026-04-02T21:33:28","slug":"creating-a-slider-in-mobile-photo-view","status":"publish","type":"post","link":"https:\/\/theeventscalendar.com\/knowledgebase\/creating-a-slider-in-mobile-photo-view\/","title":{"rendered":"Creating an Event Slider in Mobile Photo View"},"content":{"rendered":"\n<p>In this article, we\u2019ll talk about how we can work with JavaScript-based events in <a href=\"https:\/\/theeventscalendar.com\/product\/wordpress-events-calendar\/\" target=\"_blank\" rel=\"noreferrer noopener\">The Events Calendar<\/a> plugin to build a complex customization. Our goal is to turn the photo view into a slider on small screens (e.g. phones and tablets), but retain the full layout on larger screens.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"\/knowledgebase\/wp-content\/uploads\/2020\/02\/KB-photo-view-slider-landscape.gif\" alt=\"Creating a slider in Photo View\" class=\"wp-image-1945834\"\/><\/figure>\n\n\n\n<p>One thing we&#8217;ll do is adjust the slider based on whether the device is portrait view (we\u2019ll show a single event and navigation) or in landscape view (we\u2019ll show two events per slide).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-override-the-template-files\">Override the template files<\/h2>\n\n\n\n<p>Let\u2019s make a few adjustments to the template first. We&#8217;re going to override these template files in the plugin:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\n\/wp-content\/plugins\/events-pro\/src\/views\/v2\/photo.php\n\/wp-content\/plugins\/events-pro\/src\/views\/v2\/photo\/event.php\n<\/pre><\/div>\n\n\n<p>So, let&#8217;s copy those files and place them in our theme just as we would do when <a href=\"https:\/\/theeventscalendar.com\/knowledgebase\/k\/customizing-template-files-2\/\" target=\"_blank\" rel=\"noreferrer noopener\">customizing any template<\/a> in the plugin. They can go here:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\n&#x5B;your-theme]\/tribe\/events-pro\/v2\/photo.php\n&#x5B;your-theme]\/tribe\/events-pro\/v2\/photo\/event.php\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-customizing-the-templates\">Customizing the templates<\/h2>\n\n\n\n<p>Here&#8217;s the code we want to use for the <code>photo.php<\/code> file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n$header_classes = &#x5B; &#039;tribe-events-header&#039; ];\nif ( empty( $disable_event_search ) ) {\n  $header_classes&#x5B;] = &#039;tribe-events-header--has-event-search&#039;;\n}\n?&gt;\n&lt;div\n  &lt;?php tribe_classes( $container_classes ); ?&gt;\n  data-js=&quot;tribe-events-view&quot;\n  data-view-rest-nonce=&quot;&lt;?php echo esc_attr( $rest_nonce ); ?&gt;&quot;\n  data-view-rest-url=&quot;&lt;?php echo esc_url( $rest_url ); ?&gt;&quot;\n  data-view-manage-url=&quot;&lt;?php echo esc_attr( $should_manage_url ); ?&gt;&quot;\n  &lt;?php foreach ( $container_data as $key =&gt; $value ) : ?&gt;\n  data-view-&lt;?php echo esc_attr( $key ) ?&gt;=&quot;&lt;?php echo esc_attr( $value ) ?&gt;&quot;\n  &lt;?php endforeach; ?&gt;\n  &lt;?php if ( ! empty( $breakpoint_pointer ) ) : ?&gt;\n  data-view-breakpoint-pointer=&quot;&lt;?php echo esc_attr( $breakpoint_pointer ); ?&gt;&quot;\n  &lt;?php endif; ?&gt;\n&gt;\n  &lt;div class=&quot;tribe-common-l-container tribe-events-l-container&quot;&gt;\n    &lt;?php $this-&gt;template( &#039;components\/loader&#039;, &#x5B; &#039;text&#039; =&gt; __( &#039;Loading...&#039;, &#039;tribe-events-calendar-pro&#039; ) ] ); ?&gt;\n\n    &lt;?php $this-&gt;template( &#039;components\/data&#039; ); ?&gt;\n\n    &lt;?php $this-&gt;template( &#039;components\/before&#039; ); ?&gt;\n\n    &lt;header &lt;?php tribe_classes( $header_classes ); ?&gt;&gt;\n      &lt;?php $this-&gt;template( &#039;components\/messages&#039; ); ?&gt;\n\n      &lt;?php $this-&gt;template( &#039;components\/breadcrumbs&#039; ); ?&gt;\n\n      &lt;?php $this-&gt;template( &#039;components\/events-bar&#039; ); ?&gt;\n\n      &lt;?php $this-&gt;template( &#039;photo\/top-bar&#039; ); ?&gt;\n    &lt;\/header&gt;\n\n    &lt;?php $this-&gt;template( &#039;components\/filter-bar&#039; ); ?&gt;\n\n    &lt;div class=&quot;tribe-events-pro-photo swiper-container&quot;&gt;\n\n      &lt;div class=&quot;tribe-events-pro-photo__events tribe-common-g-row tribe-common-g-row--gutters swiper-wrapper&quot;&gt;\n\n      &lt;?php foreach ( $events as $event ) : ?&gt;\n        &lt;?php $this-&gt;setup_postdata( $event ); ?&gt;\n\n        &lt;?php $this-&gt;template( &#039;photo\/event&#039;, &#x5B; &#039;event&#039; =&gt; $event ] ); ?&gt;\n\n      &lt;?php endforeach; ?&gt;\n\n      &lt;\/div&gt;\n\n      &lt;button class=&quot;swiper-button-prev tribe-common-c-btn-icon tribe-common-c-btn-icon--caret-left&quot;&gt;&lt;\/button&gt;\n      &lt;button class=&quot;swiper-button-next tribe-common-c-btn-icon tribe-common-c-btn-icon--caret-right&quot;&gt;&lt;\/button&gt;\n\n      &lt;div class=&quot;swiper-pagination&quot;&gt;&lt;\/div&gt;\n\n    &lt;\/div&gt;\n\n    &lt;?php $this-&gt;template( &#039;photo\/nav&#039; ); ?&gt;\n\n    &lt;?php $this-&gt;template( &#039;components\/ical-link&#039; ); ?&gt;\n\n    &lt;?php $this-&gt;template( &#039;components\/after&#039; ); ?&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;\n\n&lt;?php $this-&gt;template( &#039;components\/breakpoints&#039; ); ?&gt;\n<\/pre><\/div>\n\n\n<p>Next, edit <code>event.php<\/code> so it looks like this<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\n&lt;?php\n$classes = get_post_class( &#x5B; &#039;tribe-common-g-col&#039;, &#039;tribe-events-pro-photo__event&#039;, &#039;swiper-slide&#039; ], $event-&gt;ID );\n\nif ( $event-&gt;featured ) {\n  $classes&#x5B;] = &#039;tribe-events-pro-photo__event--featured&#039;;\n}\n\n?&gt;\n&lt;article &lt;?php tribe_classes( $classes ) ?&gt;&gt;\n\n  &lt;?php $this-&gt;template( &#039;photo\/event\/featured-image&#039;, &#x5B; &#039;event&#039; =&gt; $event ] ); ?&gt;\n\n  &lt;div class=&quot;tribe-events-pro-photo__event-details-wrapper&quot;&gt;\n  &lt;?php $this-&gt;template( &#039;photo\/event\/date-tag&#039;, &#x5B; &#039;event&#039; =&gt; $event ] ); ?&gt;\n    &lt;div class=&quot;tribe-events-pro-photo__event-details&quot;&gt;\n      &lt;?php $this-&gt;template( &#039;photo\/event\/date-time&#039;, &#x5B; &#039;event&#039; =&gt; $event ] ); ?&gt;\n      &lt;?php $this-&gt;template( &#039;photo\/event\/title&#039;, &#x5B; &#039;event&#039; =&gt; $event ] ); ?&gt;\n      &lt;?php $this-&gt;template( &#039;photo\/event\/cost&#039;, &#x5B; &#039;event&#039; =&gt; $event ] ); ?&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n\n&lt;\/article&gt;\n<\/pre><\/div>\n\n\n<p>We haven&#8217;t made too many changes here. Let\u2019s add some styles so our slider looks good. Add the following to the <code>style.css<\/code> file in your theme, or using s <a href=\"https:\/\/theeventscalendar.com\/knowledgebase\/k\/customizing-css\/\">CSS override<\/a>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: css; title: ; notranslate\" title=\"\">\n.tribe-events-pro .tribe-events-pro-photo {\n  margin: 0 -21px;\n  overflow: hidden;\n  position: relative;\n}\n\n.tribe-common--breakpoint-medium.tribe-events-pro .tribe-events-pro-photo {\n  margin: 0;\n  padding: 0;\n}\n\n.tribe-events-pro .tribe-events-pro-photo__events {\n  flex-wrap: nowrap;\n  margin: 0;\n}\n\n.tribe-common--breakpoint-medium.tribe-events-pro .tribe-events-pro-photo__events {\n  flex-wrap: wrap;\n}\n\n.tribe-events-pro .tribe-events-pro-photo__event {\n  flex: none;\n}\n\n.tribe-events-pro .tribe-events-pro-photo .swiper-pagination {\n  display: flex;\n  justify-content: center;\n  padding: 15px 16px;\n}\n\n.tribe-events-pro .tribe-events-pro-photo .swiper-pagination-bullet {\n  background-color: #bababa;\n  border-radius: 50%;\n  flex: none;\n  height: 10px;\n  margin: 0 6px;\n  width: 10px;\n}\n\n.tribe-events-pro .tribe-events-pro-photo .swiper-pagination-bullet:hover,\n.tribe-events-pro .tribe-events-pro-photo .swiper-pagination-bullet:focus,\n.tribe-events-pro .tribe-events-pro-photo .swiper-pagination-bullet-active {\n  background-color: #727272;\n}\n\n.tribe-events-pro .tribe-events-pro-photo .swiper-button-prev,\n.tribe-events-pro .tribe-events-pro-photo .swiper-button-next {\n  position: absolute;\n  bottom: 10px;\n}\n\n.tribe-events-pro .tribe-events-pro-photo .swiper-button-prev {\n  left: 21px;\n}\n\n.tribe-events-pro .tribe-events-pro-photo .swiper-button-next {\n  right: 21px;\n}\n\n.tribe-common--breakpoint-medium.tribe-events-pro .tribe-events-pro-photo .swiper-pagination,\n.tribe-common--breakpoint-medium.tribe-events-pro .tribe-events-pro-photo .swiper-button-prev,\n.tribe-common--breakpoint-medium.tribe-events-pro .tribe-events-pro-photo .swiper-button-next {\n  display: none;\n  visibility: hidden;\n}\n<\/pre><\/div>\n\n\n<p>Now that we have all our styles set up, we need to add in some JavaScript functionality. I\u2019ll be creating my file in <code>[your-theme]\/assets\/js\/photo-swiper.js<\/code>, but you can create yours anywhere you want, as long as it contains this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nvar photoSwiper = {};\n\n( function( $, obj ) {\n  &#039;use strict&#039;;\n  var $document = $( document );\n \n  obj.selectors = {\n    slider: &#039;.tribe-events-pro-photo.swiper-container&#039;,\n  };\n \n  obj.instances = {};\n \n  obj.deinitSwiper = function( $container ) {\n    var uid = $container.attr( &#039;data-view-breakpoint-pointer&#039; )\n    $container\n    .find( obj.selectors.slider )\n    .each( function( index, slider ) {\n      if ( ! obj.instances&#x5B; uid + &#039;-&#039; + index ] ) {\n        return;\n      }\n \n      obj.instances&#x5B; uid + &#039;-&#039; + index ].destroy();\n    } );\n  };\n \n  obj.initSwiper = function( $container ) {\n    var uid = $container.attr( &#039;data-view-breakpoint-pointer&#039; );\n    $container\n    .find( obj.selectors.slider )\n    .each( function( index, slider ) {\n      if ( obj.instances&#x5B; uid + &#039;-&#039; + index ] &amp;&amp; obj.instances&#x5B; uid + &#039;-&#039; + index ].initialized ) {\n        return;\n      }\n \n      obj.instances&#x5B; uid + &#039;-&#039; + index ] = new Swiper( slider, {\n        slidesPerView: &#039;auto&#039;,\n        navigation: {\n          prevEl: slider.querySelector( &#039;.swiper-button-prev&#039; ),\n          nextEl: slider.querySelector( &#039;.swiper-button-next&#039; ),\n        },\n        pagination: {\n          el: &#039;.swiper-pagination&#039;,\n          type: &#039;bullets&#039;,\n          bulletElement: &#039;button&#039;,\n          clickable: true,\n        }\n      } );\n    } );\n  };\n \n  obj.setSwiper = function( $container ) {\n      var containerState = $container.data( &#039;tribeEventsState&#039; );\n      var isMobile = containerState.isMobile;\n \n      if ( isMobile ) {\n        obj.initSwiper( $container );\n      } else {\n        obj.deinitSwiper( $container );\n      }\n    };\n \n    obj.handleResize = function( event ) {\n      obj.setSwiper( event.data.container );\n    };\n \n    obj.bindEvents = function( $container ) {\n      $container.on( &#039;resize.tribeEvents&#039;, { container: $container }, obj.handleResize );\n    };\n \n    obj.deinit = function( event, jqXHR, settings ) {\n      var $container = event.data.container;\n      obj.deinitSwiper( $container );\n      $container.off( &#039;beforeAjaxSuccess.tribeEvents&#039;, obj.deinit )\n    };\n \n    obj.init = function( event, index, $container, data ) {\n      var $sliders = $container.find( obj.selectors.slider );\n \n      if ( ! $sliders.length ) {\n        return;\n      }\n \n      obj.setSwiper( $container );\n      obj.bindEvents( $container );\n      $container.on( &#039;beforeAjaxSuccess.tribeEvents&#039;, { container: $container }, obj.deinit );\n    };\n \n    obj.ready = function( event ) {\n      const manager = tribe.events.views.manager;\n      manager.$containers.each( function( index, container ) {\n        var $container = $( container );\n        obj.init( event, index, $container, manager.getContainerData( $container ) );\n      } );\n      $document.on( &#039;afterSetup.tribeEvents&#039;, manager.selectors.container, obj.init );\n    };\n \n    $document.ready( obj.ready );\n} )( jQuery, photoSwiper );\n<\/pre><\/div>\n\n\n<p>Phew, that\u2019s a lot of code! Let\u2019s break this down a little bit.<\/p>\n\n\n\n<p>First, we are using a great slider library called <a href=\"https:\/\/swiperjs.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Swiper<\/a>. This is a very customizable and accessible slider. However, you are more than welcome to use any other slider library. If you do, keep in mind that the templates and styles may have to change.<\/p>\n\n\n\n<p>The main interaction with The Events Calendar is through the listener for the <code>'resize.tribeEvents'<\/code> event. You can see that we aren\u2019t just hooking into the <code>'resize'<\/code> event on the <code>window<\/code> object. This is because The Events Calendar does some processing before firing the <code>'resize.tribeEvents'<\/code> event that can come in handy.<\/p>\n\n\n\n<p>The Events Calendar views have breakpoints much like media queries but do not use media queries. Instead, classes are applied based on the view container. So after every <code>'resize'<\/code> event, we do some calculations to determine whether the container is in mobile or desktop view. In the resize handler for the photo swiper, we read the <code>'isMobile'<\/code> container state variable which was processed just before firing the <code>'resize.tribeEvents'<\/code> event. This determines whether to initialize the slider or not.<\/p>\n\n\n\n<p>Lastly, we need to enqueue the required JavaScript files. Add the following code to your <code>functions.php<\/code> file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; title: ; notranslate\" title=\"\">\nfunction add_photo_swiper_js() {\n  wp_enqueue_script( &#039;swiper-js&#039;, &#039;https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/Swiper\/4.5.1\/js\/swiper.min.js&#039;, &#x5B; &#039;tribe-events-views-v2-viewport&#039; ], false, true );\n  wp_enqueue_script( &#039;custom-photo-swiper-js&#039;, get_stylesheet_directory_uri() . &#039;\/assets\/js\/photo-swiper.js&#039;, &#x5B; &#039;swiper-js&#039; ], false, true );\n}\nadd_action( &#039;wp_enqueue_scripts&#039;, &#039;add_photo_swiper_js&#039; );\n<\/pre><\/div>\n\n\n<p>Once that\u2019s done, visit your photo view to see the result. You should see a slider on mobile and a grid on desktop.<\/p>\n\n\n\n<p>Congratulations! You\u2019ve just implemented slider customization for photo view on mobile.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"\/knowledgebase\/wp-content\/uploads\/2020\/02\/KB-photo-view-slider-portrait.gif\" alt=\"Custom slider in Photo View\" class=\"wp-image-1945835\"\/><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>In this article, we\u2019ll talk about how we can work with JavaScript-based events in The Events Calendar plugin to build a complex customization. Our goal is to turn the photo view into a slider on small screens (e.g. phones and tablets), but retain the full layout on larger screens. One thing we&#8217;ll do is adjust&#8230;<\/p>\n","protected":false},"author":1,"featured_media":1955565,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","_swpsp_post_exclude":false,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"ep_exclude_from_search":false,"footnotes":""},"categories":[24,79],"tags":[25],"stellar-product-taxonomy":[158],"class_list":["post-1945830","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-customizing","category-snippets","tag-customizations","stellar-product-taxonomy-events-calendar-pro"],"acf":[],"taxonomy_info":{"category":[{"value":24,"label":"Customizations"},{"value":79,"label":"Snippets"}],"post_tag":[{"value":25,"label":"Customizations"}],"stellar-product-taxonomy":[{"value":158,"label":"Events Calendar Pro"}]},"featured_image_src_large":["https:\/\/images.theeventscalendar.com\/kb\/uploads\/2023\/02\/social-share-1024x538.png",1024,538,true],"author_info":{"display_name":"sky","author_link":"https:\/\/theeventscalendar.com\/knowledgebase\/author\/sky\/"},"comment_info":0,"category_info":[{"term_id":24,"name":"Customizations","slug":"customizing","term_group":0,"term_taxonomy_id":24,"taxonomy":"category","description":"","parent":0,"count":157,"filter":"raw","term_order":"0","cat_ID":24,"category_count":157,"category_description":"","cat_name":"Customizations","category_nicename":"customizing","category_parent":0},{"term_id":79,"name":"Snippets","slug":"snippets","term_group":0,"term_taxonomy_id":79,"taxonomy":"category","description":"","parent":0,"count":80,"filter":"raw","term_order":"0","cat_ID":79,"category_count":80,"category_description":"","cat_name":"Snippets","category_nicename":"snippets","category_parent":0}],"tag_info":[{"term_id":25,"name":"Customizations","slug":"customizations","term_group":0,"term_taxonomy_id":25,"taxonomy":"post_tag","description":"","parent":0,"count":102,"filter":"raw","term_order":"0"}],"_links":{"self":[{"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/posts\/1945830","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/comments?post=1945830"}],"version-history":[{"count":2,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/posts\/1945830\/revisions"}],"predecessor-version":[{"id":1968453,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/posts\/1945830\/revisions\/1968453"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/media\/1955565"}],"wp:attachment":[{"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/media?parent=1945830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/categories?post=1945830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/tags?post=1945830"},{"taxonomy":"stellar-product-taxonomy","embeddable":true,"href":"https:\/\/theeventscalendar.com\/knowledgebase\/wp-json\/wp\/v2\/stellar-product-taxonomy?post=1945830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}