New Shopify Certification now available: Liquid Storefronts for Theme Developers

Thumbnails do not receive alt tags.

Paul_Stewart
Shopify Partner
25 0 7

Hello, 

We have an urgent situation regarding alt tags. 

After running an audit on our site, we have noticed that every product that DOES have alt tags still throws an error.  Please see attached image.Screen Shot 2019-03-14 at 10.43.27 AM.jpg

I've tested this in a different theme as well and found the same results. (that the thumbnails are not getting properly tagged)

The following is code for product.thumbnail - please let me know if you see anything that might be askew.  This is from the Out of the Sandbox theme "Retina"

 

{% if sidebar %}
  <div class="{% if products_per_row == 2 %}six columns {% cycle collection_group_thumb: 'alpha', 'omega' %}{% elsif products_per_row == 3 %}four columns {% cycle collection_group_thumb: 'alpha', '', 'omega' %}{% else %}three columns {% cycle collection_group_thumb: 'alpha', '', '', 'omega' %}{% endif %} thumbnail {% cycle collection_group_mobile: 'even', 'odd' %}" itemprop="itemListElement" itemscope itemtype="http://schema.org/Product">
{% else %}
  <div class="{% if products_per_row == 2 %}eight columns {% cycle collection_group_thumb: 'alpha', 'omega' %}{% elsif products_per_row == 3 %}one-third column {% cycle collection_group_thumb: 'alpha', '', 'omega' %}{% else %}four columns {% cycle collection_group_thumb: 'alpha', '', '', 'omega' %}{% endif %} thumbnail {% cycle collection_group_mobile: 'even', 'odd' %}" itemprop="itemListElement" itemscope itemtype="http://schema.org/Product">
{% endif %}

  {% assign collection_handles = product.collections | map: 'handle' %}
  {% if product.featured_image.alt contains 'youtube' or image.alt contains 'vimeo' %}
    {% assign alt_text = product.title %}
  {% else %}
    {% assign alt_text = product.featured_image.alt %}
  {% endif %}

  {% if settings.align_height %}
    {% assign collection_height = settings.collection_height | plus: 0 %}
    {% assign product_aspect_ratio = product.featured_image.aspect_ratio | plus: 0 %}
    {% assign product_set_width = product_aspect_ratio | times: collection_height %}

    {% if product.featured_image.width >= product.featured_image.height %}
      {% assign align_height_value = 'width: 100%; height: auto;' %}
    {% else %}
      {% assign align_height_value = 'width: 100%;' %}
    {% endif %}

  {% endif %}

  <a href="{{ product.url | within: collection }}" itemprop="url">
    <div class="relative product_image aa">
      <div class="image__container" style="{% if settings.align_height %} max-width: {{ product_set_width }}px; {% else %} max-width: {{- product.featured_image.width -}}px; {% endif %}">
        <img  src="{{ product.featured_image | product_img_url: '100x' }}"
              alt="{{ alt_text | escape }}"
              class="lazyload lazyload--mirage primary"
              style="{{ align_height_value }} max-width: {{- product.featured_image.width -}}px;"
              data-sizes="auto"
              data-src="{{ product.featured_image | product_img_url: '2048x' }}"
              data-srcset=" {{ product.featured_image | product_img_url: '2048x' }} 2048w,
                            {{ product.featured_image | product_img_url: '1600x' }} 1600w,
                            {{ product.featured_image | product_img_url: '1200x' }} 1200w,
                            {{ product.featured_image | product_img_url: '1000x' }} 1000w,
                            {{ product.featured_image | product_img_url: '800x' }} 800w,
                            {{ product.featured_image | product_img_url: '600x' }} 600w,
                            {{ product.featured_image | product_img_url: '400x' }} 400w"
            />
          <div class="transition-helper">
          </div>
      </div>

      {% if settings.collection_secondary_image %}
        <img src="{% if product.images[1] != blank %}{{ product.images[1] | product_img_url: '580x', scale: 2 }}{% else %}{{ product.featured_image | product_img_url: '580x', scale: 2 }}{% endif %}" class="secondary" alt="{{ alt_text | escape }}" />
      {% endif %}

      {% if settings.quick_shop_enabled %}
        {% unless collection_handles contains 'japan' or collection_handles contains 'bundles' or collection_handles contains 'bundle' or product.handle contains 'bundle' or product.handle contains 'redpoint' or collection_handles contains 'stocking' %}
          <span data-fancybox-href="#product-{{ product.id }}" class="quick_shop action_button" data-gallery="product-{{ product.id }}-gallery">
            {{ 'collections.general.quick_shop' | t }}
          </span>
        {% endunless %}
      {% endif %}
    </div>
    <div class="info">
      <span class="title" itemprop="name">{{ product.title }}</span>
      
      

              <div style="display:none;">
          {{ products_per_row }}
        </div>
      {% if settings.enable_shopify_collection_badges %}
      {% comment %}
          <span class="shopify-product-reviews-badge" data-id="{{ product.id }}"></span>
      {% endcomment %}
        {% endif %}
      {% if settings.display_vendor_collection %}
        <br />
        <span itemprop="brand">{{ product.vendor }}</span>
      {% endif %}

      {% include 'judgeme_widgets', widget_type: 'judgeme_preview_badge', jm_style: '', concierge_install: true %}
      {% unless collection_handles contains 'coming-soon' or collection_handles contains 'japan' %}
        <span class="price {% if product.compare_at_price_max > product.price %}sale{% endif %}" itemprop="offers" itemscope itemtype="http://schema.org/Offer">
          <meta itemprop="price" content="{{ product.price_min | money_without_currency }}" />
          <meta itemprop="priceCurrency" content="{{ shop.currency }}" />
          <meta itemprop="seller" content="{{ shop.name | escape }}" />
          <link itemprop="availability" href="http://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}">
          <meta itemprop="itemCondition" content="New" />

          {% if product.available %}
            {% if product.price_varies and product.price_min > 0 %}
              <small><em>{{ 'products.general.from' | t }}</em></small>
            {% endif %}
            {% if product.price_min > 0 %}
              <span class="money">{{ product.price_min | money }}</span>
            {% else %}
              {{ settings.free_price_text }}
            {% endif %}
            {% if product.compare_at_price_max > product.price %}
              <span class="was_price">
                <span class="money">{{ product.compare_at_price_max | money }}</span>
              </span>
            {% endif %}
          {% else %}
            <span class="sold_out">{{ 'products.product.sold_out' | t }}</span>
          {% endif %}
        </span>
      {% endunless %}
    </div>
    {% if settings.sale_banner_enabled and product.compare_at_price_max > product.price %}
      <div class="sale_banner">{{ 'collections.general.sale' | t }}</div>
    {% endif %}

    {% if collection_handles contains 'new' %}
      <div class="new_banner">{{ 'collections.general.new' | t }}</div>
    {% endif %}
    {% if collection_handles contains 'coming-soon' %}
      <div class="new_banner">{{ 'collections.general.coming_soon' | t }}</div>
    {% endif %}
    {% if collection_handles contains 'pre-order' %}
      <div class="new_banner">{{ 'collections.general.pre_order' | t }}</div>
    {% endif %}
  </a>
  {% include 'collection-swatch' %}
</div>

{% if settings.quick_shop_enabled %}
  {% if product.description contains "#tab1" %}
    {% assign tabs = true %}
  {% else %}
    {% assign tabs = false %}
  {% endif %}

  <div id="product-{{ product.id }}" class="modal product-{{ product.id }} product_section thumbnail_position--{{settings.thumbnail_position}} product_slideshow_animation--{{settings.product_slideshow_animation}}"
       data-thumbnail="{{settings.thumbnail_position}}"
       data-slideshow-animation="{{settings.product_slideshow_animation}}"
       data-slideshow-speed="{{settings.slideshow_speed}}">
    <div class="container" style="width: inherit">

      <div class="eight columns" style="padding-left: 15px">
        {% include 'product-images' with 'modal' %}
      </div>

      <div class="six columns">
        <h3>{{ product.title }}</h3>
        {% if settings.enable_shopify_collection_badges %}
          <span class="shopify-product-reviews-badge" data-id="{{ product.id }}"></span>
        {% endif %}
        {% if settings.display_vendor_collection %}
          <p class="vendor">
            <span itemprop="brand">{{ product.vendor | link_to_vendor }}</span>
          </p>
        {% endif %}

        {% assign variant = product.selected_or_first_available_variant %}

        {% unless collection_handles contains 'coming-soon' %}
          <p class="modal_price">
            <span class="sold_out">{% if variant.available == false %}{{ 'products.product.sold_out' | t }}{% endif %}</span>
            <span class="{% if variant.compare_at_price > variant.price %}sale{% endif %}">
              <span class="current_price {% if product.available == false %}hidden{% endif %}">
                {% if variant.price > 0 %}
                  <span class="money">{{ variant.price | money }}</span>
                {% else %}
                  {{ settings.free_price_text }}
                {% endif %}
              </span>
            </span>
            <span class="was_price">
              {% if variant.price < variant.compare_at_price and variant.available %}
                <span class="money">{{ variant.compare_at_price | money }}</span>
              {% endif %}
            </span>
          </p>

          {% include 'product-notify-me' %}
        {% endunless %}
        {% if settings.product_description_position == "top" %}
          {% if settings.description_words == "none" %}
            {{ product.description | split: '<!-- split -->' | first }}
          {% else %}
            {% if tabs %}
              {{ product.description | split: '<!-- split -->' | first }}
            {% else %}
              <p>{{ product.description | strip_html | truncatewords: settings.description_words }}</p>
            {% endif %}
          {% endif %}
          <p>
            <a href="{{ product.url | within: collection }}" class="view_product_info" title="{{ product.title | escape }}">{{ 'collections.general.view_product_details_html' | t }}</a>
          </p>
          <hr />
        {% endif %}

        {% unless collection_handles contains 'coming-soon' %}
          {% include 'product-form' %}
        {% endunless %}

        {% if settings.product_description_position == "bottom" %}
          <hr />
          {% if settings.description_words == "none" %}
            {{ product.description | split: '<!-- split -->' | first }}
          {% else %}
            {% if tabs %}
              {{ product.description | split: '<!-- split -->' | first }}
            {% else %}
              <p>{{ product.description | strip_html | truncatewords: settings.description_words }}</p>
            {% endif %}
          {% endif %}
          <p>
            <a href="{{ product.url | within: collection }}" class="view_product_info" title="{{ product.title | escape }}">{{ 'collections.general.view_product_details_html' | t }}</a>
          </p>
        {% endif %}
      </div>
    </div>
  </div>
{% endif %}

 

Replies 2 (2)
Paul_Stewart
Shopify Partner
25 0 7

I'm thinking it might have something to do with the app.js.liquid file.  Specifically the Flexslider code.

Please see the following link as well as the following code.

this is dealing w/ drupal, but it's the same idea https://www.drupal.org/project/flexslider/issues/2627692

p.s. I had to get rid of some of the flex slider code because of message limitations.  however, the parts addressing data-thumb-alt are still contained.

 

* jQuery FlexSlider v2.6.1
 * Copyright 2012 WooThemes
 * Contributing Author: Tyler Smith
 */!function($){var e=!0;$.flexslider=function(t,a){var n=$(t);n.vars=$.extend({},$.flexslider.defaults,a);var i=n.vars.namespace,s=window.navigator&&window.navigator.msPointerEnabled&&window.MSGesture,r=("ontouchstart"in window||s||window.DocumentTouch&&document instanceof DocumentTouch)&&n.vars.touch,o="click touchend MSPointerUp keyup",l="",c,d="vertical"===n.vars.direction,u=n.vars.reverse,v=n.vars.itemWidth>0,p="fade"===n.vars.animation,m=""!==n.vars.asNavFor,f={};$.data(t,"flexslider",n),f={init:function(){n.animating=!1,n.currentSlide=parseInt(n.vars.startAt?n.vars.startAt:0,10),isNaN(n.currentSlide)&&(n.currentSlide=0),n.animatingTo=n.currentSlide,n.atEnd=0===n.currentSlide||n.currentSlide===n.last,n.containerSelector=n.vars.selector.substr(0,n.vars.selector.search(" ")),n.slides=$(n.vars.selector,n),n.container=$(n.containerSelector,n),n.count=n.slides.length,n.syncExists=$(n.vars.sync).length>0,"slide"===n.vars.animation&&(n.vars.animation="swing"),n.prop=d?"top":"marginLeft",n.args={},n.manualPause=!1,n.stopped=!1,n.started=!1,n.startTimeout=null,n.transitions=!n.vars.video&&!p&&n.vars.useCSS&&function(){var e=document.createElement("div"),t=["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"];for(var a in t)if(void 0!==e.style[t[a]])return n.pfx=t[a].replace("Perspective","").toLowerCase(),n.prop="-"+n.pfx+"-transform",!0;return!1}(),n.ensureAnimationEnd="",""!==n.vars.controlsContainer&&(n.controlsContainer=$(n.vars.controlsContainer).length>0&&$(n.vars.controlsContainer)),""!==n.vars.manualControls&&(n.manualControls=$(n.vars.manualControls).length>0&&$(n.vars.manualControls)),""!==n.vars.customDirectionNav&&(n.customDirectionNav=2===$(n.vars.customDirectionNav).length&&$(n.vars.customDirectionNav)),n.vars.randomize&&(n.slides.sort(function(){return Math.round(Math.random())-.5}),n.container.empty().append(n.slides)),n.doMath(),n.setup("init"),n.vars.controlNav&&f.controlNav.setup(),n.vars.directionNav&&f.directionNav.setup(),n.vars.keyboard&&(1===$(n.containerSelector).length||n.vars.multipleKeyboard)&&$(document).bind("keyup",function(e){var t=e.keyCode;if(!n.animating&&(39===t||37===t)){var a=39===t?n.getTarget("next"):37===t?n.getTarget("prev"):!1;n.flexAnimate(a,n.vars.pauseOnAction)}}),n.vars.mousewheel&&n.bind("mousewheel",function(e,t,a,i){e.preventDefault();var s=0>t?n.getTarget("next"):n.getTarget("prev");n.flexAnimate(s,n.vars.pauseOnAction)}),n.vars.pausePlay&&f.pausePlay.setup(),n.vars.slideshow&&n.vars.pauseInvisible&&f.pauseInvisible.init(),n.vars.slideshow&&(n.vars.pauseOnHover&&n.hover(function(){n.manualPlay||n.manualPause||n.pause()},function(){n.manualPause||n.manualPlay||n.stopped||n.play()}),n.vars.pauseInvisible&&f.pauseInvisible.isHidden()||(n.vars.initDelay>0?n.startTimeout=setTimeout(n.play,n.vars.initDelay):n.play())),m&&f.asNav.setup(),r&&n.vars.touch&&f.touch(),(!p||p&&n.vars.smoothHeight)&&$(window).bind("resize orientationchange focus",f.resize),n.find("img").attr("draggable","false"),setTimeout(function(){n.vars.start(n)},200)},asNav:{setup:function(){n.asNav=!0,n.animatingTo=Math.floor(n.currentSlide/n.move),n.currentItem=n.currentSlide,n.slides.removeClass(i+"active-slide").eq(n.currentItem).addClass(i+"active-slide"),s?(t._slider=n,n.slides.each(function(){var e=this;e._gesture=new MSGesture,e._gesture.target=e,e.addEventListener("MSPointerDown",function(e){e.preventDefault(),e.currentTarget._gesture&&e.currentTarget._gesture.addPointer(e.pointerId)},!1),e.addEventListener("MSGestureTap",function(e){e.preventDefault();var t=$(this),a=t.index();$(n.vars.asNavFor).data("flexslider").animating||t.hasClass("active")||(n.direction=n.currentItem<a?"next":"prev",n.flexAnimate(a,n.vars.pauseOnAction,!1,!0,!0))})})):n.slides.on(o,function(e){e.preventDefault();var t=$(this),a=t.index(),s=t.offset().left-$(n).scrollLeft();0>=s&&t.hasClass(i+"active-slide")?n.flexAnimate(n.getTarget("prev"),!0):$(n.vars.asNavFor).data("flexslider").animating||t.hasClass(i+"active-slide")||(n.direction=n.currentItem<a?"next":"prev",n.flexAnimate(a,n.vars.pauseOnAction,!1,!0,!0))})}},controlNav:{setup:function(){n.manualControls?f.controlNav.setupManual():f.controlNav.setupPaging()},setupPaging:function(){var e="thumbnails"===n.vars.controlNav?"control-thumbs":"control-paging",t=1,a,s;if(n.controlNavScaffold=$('<ol class="'+i+"control-nav "+i+e+'"></ol>'),n.pagingCount>1)for(var r=0;r<n.pagingCount;r++){s=n.slides.eq(r),void 0===s.attr("data-thumb-alt")&&s.attr("data-thumb-alt","");var c=""!==s.attr("data-thumb-alt")?c=' alt="'+s.attr("data-thumb-alt")+'"':"";if(a="thumbnails"===n.vars.controlNav?'<img src="'+s.attr("data-thumb")+'"'+c+"/>":'<a href="#">'+t+"</a>","thumbnails"===n.vars.controlNav&&!0===n.vars.thumbCaptions){var d=s.attr("data-thumbcaption");""!==d&&void 0!==d&&(a+='<span class="'+i+'caption">'+d+"</span>")}n.controlNavScaffold.append("<li>"+a+"</li>"),t++}n.controlsContainer?$(n.controlsContainer).append(n.controlNavScaffold):n.append(n.controlNavScaffold),f.controlNav.set(),f.controlNav.active(),n.controlNavScaffold.delegate("a, img",o,function(e){if(e.preventDefault(),""===l||l===e.type){var t=$(this),a=n.controlNav.index(t);t.hasClass(i+"active")||(n.direction=a>n.currentSlide?"next":"prev",n.flexAnimate(a,n.vars.pauseOnAction))}""===l&&(l=e.type),f.setToClearWatchedEvent()})},setupManual:function(){n.controlNav=n.manualControls,f.controlNav.active(),n.controlNav.bind(o,function(e){if(e.preventDefault(),""===l||l===e.type){var t=$(this),a=n.controlNav.index(t);t.hasClass(i+"active")||(a>n.currentSlide?n.direction="next":n.direction="prev",n.flexAnimate(a,n.vars.pauseOnAction))}""===l&&(l=e.type),f.setToClearWatchedEvent()})},set:function(){var e="thumbnails"===n.vars.controlNav?"img":"a";n.controlNav=$("."+i+"control-nav li "+e,n.controlsContainer?n.controlsContainer:n)},active:function(){n.controlNav.removeClass(i+"active").eq(n.animatingTo).addClass(i+"active")},update:function(e,t){n.pagingCount>1&&"add"===e?n.controlNavScaffold.append($('<li><a href="#">'+n.count+"</a></li>")):1===n.pagingCount?n.controlNavScaffold.find("li").remove():n.controlNav.eq(t).closest("li").remove(),

 

 

DawnDesignPaul
Tourist
8 1 1

Incase anyone comes across this issue, this worked: https://github.com/woocommerce/FlexSlider/issues/1389#issuecomment-161487988