hotspots

Solved

hotspots

rehamessawy
Visitor
3 0 0

MULTICOLUMNSECTION.PNGI want to a add hotspots circles on these two images in multicolumn section. the purpose of the hotspots is to display the price tag when the user hover on the hotspot item.

Accepted Solution (1)

Huptech-Web
Shopify Partner
908 186 189

This is an accepted solution.

Hi @rehamessawy 
You can follow the below instructions to make the image hotspot

 

Step 1: Create a section and name it tooltips So once saved, It will be tooltips.liquid

Step 2: Add the below code to the tooltips.liquid

 

{%- comment -%} ---------------- THE CSS --------------------- {%- endcomment -%}

{%- assign button_width_small = 28 -%}
{%- assign button_width_large = 20 -%}
{%- assign tooltip_max_width = 320 -%}
{%- assign image_aspect_ratio = section.settings.image.aspect_ratio | default: 1 -%}
{%- assign section_selector = '[data-tooltips="' | append: section.id | append: '"]'-%}
{%- style -%}
  .section-{{ section.id }}-padding {
    padding-top: {{ section.settings.padding_top | times: 0.5 | round: 0 }}px;
    padding-bottom: {{ section.settings.padding_bottom | times: 0.5 | round: 0 }}px;
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top }}px;
      padding-bottom: {{ section.settings.padding_bottom }}px;
    }
  }
{%- endstyle -%}
<style>
  main
  .tooltips-section {position: relative;}
  .tooltips-figure {margin: 0;}
  main#MainContent {overflow: hidden;}
  .hw-tooltip--productImage {position: relative;width: 18rem;min-width: 18rem;min-height: 27rem;}
  .hw-tooltip--productImage img {position: absolute;width: 100%;height: 100%;inset: 0;object-fit: cover;}
  .hw-tooltip--content .badge {display: none;}
  .hw-tooltip--content .price .price-item {margin: 0;font-size: 1.4rem;font-family: var(--body-textBold);}
  body .hw-tooltip--content .price--on-sale .price-item--regular {font-size: 1.4rem;font-family: var(--body-text);}
  .hw-tooltip--content {display: flex;flex-direction: column;justify-content: center;padding: .5rem;row-gap: .5rem;}
  .hw-tooltip--content .product__title {font-size: 1.4rem;}
  .hw-tooltip--content .price--on-sale .price__sale {display: flex;flex-direction: row-reverse;align-items: center;column-gap: .5rem;}
  .tooltip-close {position: absolute;right: 0;top: 0;width: 4rem;height: 4rem;color: var(--dark-900);cursor: pointer;z-index: 1;display: flex;justify-content: center;align-items: center;}
  .tooltip-close:hover {color: var(--primary-900);}
  .hw-tooltip--content .price .badge {display: none;}
  .hw-tooltip--content .product__title.hw-link.hw-link--arrow {padding-left: 0;margin-top: .5rem;}
  .hw-tooltip--content .product__title > .body-textBold {color: var(--dark-800);line-height: 1.4;}
  .tooltips-img {display: block;width: 100%;}
  .tooltips-list {padding: 0 0 0 32px;list-style: decimal;}
  .tooltip-item {box-sizing: border-box;padding: 8px 12px;}
  .tooltip-button {background: transparent;width: 100%;border: 0;padding: 0;text-align: left;z-index: 1;}
  .tooltip-button:focus {outline: none;}
  .tooltip-index {border-radius: 50%;text-align: center;position: absolute;padding: 0;
   -webkit-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
    line-height: {{ button_width_small }}px;
    width: {{ button_width_small }}px;
    cursor: pointer
  }
  .tooltip-index::before {content: '';position: absolute;width: 1.5rem;height: 1.5rem;border-radius: 100vh;inset: 0;margin: auto;}
 .tooltip-index::after {content: '';position: absolute;width: 1rem;height: 1rem;border-radius: 100vh;inset: 0;margin: auto;animation: pulse-animation 3s infinite;z-index: -1;}
@keyframes pulse-animation {
  0% {
    box-shadow: 0 0 0 0px rgba(154, 33, 31, 0.6);
    
  }
  100% {
    box-shadow: 0 0 0 20px rgba(154, 33, 31, 0);
  }
}
  .tooltip-overlay {background: var(--light-900);opacity: 1;overflow: hidden;will-change: height;}
  .tooltip-header {display: none;}
  
  
  {{ section_selector }} .tooltip-index::before {
    background-color: {{ section.settings.tooltip_background_color }};
  }
  
  {%- for block in section.blocks -%}
  {{ section_selector }} .tooltip-item:nth-child({{ forloop.index }}) .tooltip-index {
    top: 0px;
    margin-top: {{ block.settings.top | divided_by: image_aspect_ratio }}%;
    left: {{ block.settings.left }}%;
  }
  {%- endfor -%}
  
  .tooltip-item .tooltip-overlay:empty {animation: none;}
  
  [aria-expanded="true"] ~ .tooltip-overlay {
    animation: tooltip-expand 200ms both cubic-bezier(0.4, 0, 0.2, 1);
  }
  
  [aria-expanded="false"] ~ .tooltip-overlay {
    animation: tooltip-collapse 180ms both cubic-bezier(0.4, 0, 0.2, 1);
  }
  
  @keyframes tooltip-expand {
    0% { height: var(--start-h) }
    100% { height: var(--end-h) }
  }
  
  @keyframes tooltip-collapse {
    0% { height: var(--start-h) }
    100% { height: var(--end-h) }
  }

  @media (prefers-reduced-motion: reduce) {
    .tooltip-overlay {
      animation-duration: 0s!important;
    }
  }
    .tooltips-list {margin: 0;padding: 0;list-style: none;}
    .tooltip-button .tooltip-index {font-size: 0;color: rgba(0,0,0,0);position: static;transform: translate(0, 0);line-height: 1;height: {{ button_width_large }}px;width: {{ button_width_large }}px;}
    .tooltip-button {position: absolute;padding: 0;font-size: 16px;-webkit-transform: translate(-50%, -50%);transform: translate(-50%, -50%);width: {{ button_width_large }}px;height: {{ button_width_large }}px;}
    .tooltip-title {display: none;}
    .tooltip-item {max-width: {{ tooltip_max_width }}px;padding: 0;}
    [aria-expanded="true"] ~ .tooltip-overlay {margin-left: -{{ button_width_large }}px;z-index: 3;}
    [aria-expanded="true"].tooltip-button {z-index: 4;}
    .tooltip-overlay:empty {padding: 0;opacity: 0;transform: scale(0, 0);}
    .tooltip-overlay {max-width: 41.5rem;padding: 0;border-radius: .5rem;transform-origin: top left;position: absolute;will-change: opacity, transform;display: flex;}
    .tooltip-header {display: block;}
   .hw-tooltip--content .price .price-item {font-size: 1.4rem;}
    [aria-expanded="true"] ~ .tooltip-overlay {animation: tooltip-expand-large 200ms both cubic-bezier(0.4, 0, 0.2, 1);}
    [aria-expanded="false"] ~ .tooltip-overlay {animation: tooltip-collapse-large 180ms both cubic-bezier(0.4, 0, 0.2, 1);}
    
    @keyframes tooltip-expand-large {
      0% { opacity: 0; transform: scale(.2, .2); }
      100% { opacity: 1; transform: scale(1, 1); }
    }
    
    @keyframes tooltip-collapse-large {
      0% { opacity: 1; }
      100% { opacity: 0; }
    }
    
    {%- for block in section.blocks -%}
      {%- assign y = block.settings.top | divided_by: image_aspect_ratio -%}
      {%- assign tooltip_selector = '#tooltip-' | append: block.id -%}

      {{ tooltip_selector }} .tooltip-button {
        top: 0px;
        margin-top: {{ y }}%;
        left: {{ block.settings.left }}%;
      }

      {{ tooltip_selector }} .tooltip-overlay {
        top: calc(-{{ button_width_large }}px + 40px);
        margin-top: {{ y }}%;
        left: {{ block.settings.left }}%;
        {% if block.settings.right != 0 %}
          left: auto;
          right: {{ block.settings.right }}%;
          {% endif %}
      }

      {{ tooltip_selector }} .tooltip-button .tooltip-index {
        margin-top: 0;
      }
    {%- endfor -%}
.hw-tooltip--productImage {background-color: var(--light-800);}
@media screen and (min-width: 990px) {
.tooltip-overlay {min-width: 41.5rem;}
.hw-tooltip--content {flex: 1 1 100%;padding: 1.2rem 1.5rem;}  
}
@media screen and (min-width: 750px) {
.hw-tooltip--content .product__title:not(.hw-link) {font-size: 1.8rem;line-height: 2.8rem;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 3;overflow: hidden;}
.hw-tooltip--content .product__title > .body-textBold {line-height: inherit;}
body .hw-tooltip--content .price--on-sale .price-item--regular {font-size: 1.6rem;}
.hw-tooltip--content .price .price-item {font-size: 1.8rem;}
}  
@media screen and (max-width: 749px) {
  .tooltip-overlay {flex-direction: column;max-width: 18rem;}
  .hw-tooltip--productImage {position: relative;width: 100%;min-width: 100%;min-height: 27rem}
  .hw-tooltip--content {padding: 1.2rem;}
  .tooltip-index::before {width: .8rem;height: .8rem;}
  .tooltip-index::after {width: .5rem;height: .5rem;animation: pulse-animation 2s infinite;}
}  
</style>


{%- comment -%} ---------------- THE NO-JS ------------------- {%- endcomment -%}

<noscript>
  <style>
    .tooltips-section .tooltips-list {list-style: decimal;padding: 24px;}
    .tooltip-item {position: static;padding: 16px;max-width: none;}
  </style>
</noscript>


{%- comment -%} ---------------- THE MARKUP ------------------ {%- endcomment -%}
<div class="section-{{ section.id }}-padding">
<div class="page-width">
<div class="tooltips-section" data-tooltips="{{ section.id }}">
  
  <figure class="tooltips-figure">
    {%- if section.settings.image == blank -%}
      {{ 'image' | placeholder_svg_tag: 'tooltips-picture' }}
    {%- else -%}
    <picture class="tooltips-picture">
      <source srcset="{{ section.settings.image | img_url: '320x' }} 1x,
                      {{ section.settings.image | img_url: '320x', scale: 2 }} 2x" media="(max-width: 320px)">
      <source srcset="{{ section.settings.image | img_url: '420x' }} 1x,
                      {{ section.settings.image | img_url: '420x', scale: 2 }} 2x" media="(max-width: 420px)">
      <source srcset="{{ section.settings.image | img_url: '768x' }} 1x,
                      {{ section.settings.image | img_url: '768x', scale: 2 }} 2x" media="(max-width: 768px)">
      <source srcset="{{ section.settings.image | img_url: '1240x' }} 1x,
                      {{ section.settings.image | img_url: '1240x', scale: 2 }} 2x" media="(max-width: 1240px)">
      <source srcset="{{ section.settings.image | img_url: '1440x' }} 1x,
                      {{ section.settings.image | img_url: '1440x', scale: 2 }} 2x" media="(max-width: 1440px)">
      <source srcset="{{ section.settings.image | img_url: '1660x' }} 1x,
                      {{ section.settings.image | img_url: '1660x', scale: 2 }} 2x" media="(max-width: 1660px)">
      <source srcset="{{ section.settings.image | img_url: '2048x' }} 1x,
                      {{ section.settings.image | img_url: '2048x', scale: 2 }} 2x" media="(min-width: 1661px)">
      <img class="tooltips-img" src="{{ section.settings.image | img_url: '2048x' }}" alt="{{ section.settings.image.alt }}">
    </picture>
    {%- endif -%}
  </figure>
  
  <ol class="tooltips-list" aria-label="{{ section.settings.title }}">
    {%- for block in section.blocks -%}
      {% if block.settings.selected_product %}
        {% assign product = block.settings.selected_product %}
    <li class="tooltip-item" id="tooltip-{{ block.id }}">
      <button class="tooltip-button"
              type="button" 
              value="{{ block.id }}"
              aria-describedby="tooltip-overlay-{{ block.id }}" 
              aria-label="{{ forloop.index }}, {{ block.settings.title }}" 
              aria-expanded="false" 
              data-tooltip-trigger
              {{ block.shopify_attributes }} >
        <div class="tooltip-index" role="none">{{ forloop.index }}</div>
        
        <strong class="tooltip-title" role="none">{{ block.settings.title }}</strong>
      </button>
      <aside class="tooltip-overlay" id="tooltip-overlay-{{ block.id }}" data-tooltip-overlay></aside>
      
      
      
      <noscript data-tooltip-markup>
      <div class="tooltip-close" data-tooltip-close>{% render 'icon-close-large' %}</div>
        <div class="hw-tooltip--productImage"><img width="180" heigh="180" src="{{ product.featured_image | img_url:'180x' }}" /></div>
        <div class="hw-tooltip--content">
        <div class="product__title" {{ block.shopify_attributes }}>
                <div class="body-textBold">{{ product.title | escape }}</div>
              </div>
      <div class="no-js-hidden" id="price-{{ section.id }}" role="status" {{ block.shopify_attributes }}>
                {%- render 'price',
                  product: product,
                  use_variant: true,
                  show_badges: true,
                  price_class: 'price--large'
                -%}
              </div>
        <a {% if block.settings.new_window %}
                          target="_blank"
                        {% endif %} href="{{ product.url }}" class="product__title hw-link hw-link--arrow">
                 
                    {% if block.settings.button_label != blank %}
                      {{ block.settings.button_label }}
                    {% else %}
                      {{ 'products.product.tooltip_button' | t }}
                    {% endif %}
                 
                </a>
        </div>
      </noscript>
        
    </li>
        {% endif %}
    {%- endfor -%}
  </ol>
  
  
{%- comment -%} ---------------- THE CONFIG ------------------ {%- endcomment -%}
  
  <script data-tooltips-config type="application/json">
   {
     "breakpoint": 768,
     "sectionId": {{ section.id | json }},
     "blocksId": {{ section.blocks | map: 'id' | json }}
   }
  </script>

</div>
</div>
</div>
{%- comment -%} ------------------ THE JS -------------------- {%- endcomment -%}

<script src="{{ 'tooltips.js' | asset_url }}" defer="defer"></script>


{%- comment -%} --------------- THEME EDITOR ----------------- {%- endcomment -%}

{%- if section.blocks.last.shopify_attributes != blank -%}
<script>
  (function ThemeEditor(SD){
    var sectionId = {{ section.id | json }};
    if (SD.ThemeEditor[sectionId]) return;
    
    SD.ThemeEditor[sectionId] = 'init';
    initEvents(sectionId);
    
    document.addEventListener('shopify:section:load', function(evt) {
      if (evt.detail.sectionId !== sectionId) return;
      
      var section = SD[sectionId];
      section.init(section.config);
      initEvents(sectionId);
    });

    function initEvents(sectionId) {
      var sectionHost = document.querySelector('[data-tooltips="' + sectionId + '"]');

      sectionHost.addEventListener('shopify:block:select', toggleSelect);
      sectionHost.addEventListener('shopify:block:deselect', toggleSelect); 
    }
    
    function toggleSelect(evt) {
      var sectionId = evt.detail.sectionId;
      var blockId = evt.detail.blockId;
      var section = SD[sectionId];
      
      evt.type === 'shopify:block:select'
      ? section.select(blockId)
      : section.deselect(blockId)
      
      updateBlocks(section, blockId);
    }
    
    function updateBlocks(section, blockId) {
      if (section.config.blocksId.indexOf(blockId) === -1) {
        section.config.blocksId.push(blockId);
      }
    }
 
  })(window.SectionsDesign = window.SectionsDesign || {ThemeEditor: []});

</script>
{%- endif -%}

{%- comment -%} ---------------- THE SETTINGS ---------------- {%- endcomment -%}

{% schema %}
{
  "name": "Image Tag",
  "class": "sd-tooltips",
  "settings": [
    {
      "type": "paragraph",
      "content": "Image tag section"
    },
    {
      "type": "image_picker",
      "id": "image",
      "label": "Background image"
    },
    {
      "type": "color",
      "id": "tooltip_background_color",
      "label": "Dots background color",
      "default": "#64cbdf"
    },
    {
      "type": "header",
      "content": "t:sections.all.padding.section_padding_heading"
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top",
      "default": 0
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_bottom",
      "default": 0
    }
  ],
  "blocks": [
    {
      "type": "Tooltip",
      "name": "Tag",
      "settings": [
        {
          "type": "product",
          "id": "selected_product",
          "label": "Select product to show on this Tag"
        },
        {
          "type": "range",
          "id": "top",
          "min": 0,
          "max": 100,
          "step": 1,
          "unit": "%",
          "label": "Top position",
          "default": 50
        },
        {
          "type": "range",
          "id": "left",
          "min": 0,
          "max": 100,
          "step": 1,
          "unit": "%",
          "label": "Left position",
          "default": 50
        },
         {
          "type": "range",
          "id": "right",
          "min": 0,
          "max": 100,
          "step": 1,
          "unit": "%",
          "label": "Right position",
          "default": 0
        },
        {
          "type": "text",
          "id": "button_label",
          "label": "Button Label"
        },
        {
          "type": "checkbox",
          "id": "new_window",
         "default":false,
          "label": "Open link in new window"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Image Tag",
      "blocks": [
        {
          "type": "Tooltip"
        }
      ]
    }
  ]
}
{% endschema %}

 

 

Step 3: Go to assets and add JS file and name it tooltips So once saved, It will be tooltips.js

Step 4: Add the below code to the tooltips.js

 

 

(function Tooltips(SD){
  'use strict';
  
  var support = getSupport();
  var instances = nodeList('[data-tooltips-config]');

  instances.map(function(instance){
    var config = Object.freeze(getConfig(instance));

    var publicAPI = {
      tooltips: function() { return getTooltips(config) },
      tooltip: getTooltip,
      select: select,
      deselect: deselect,
      config: config,
      init: init
    }

    if (!SD[config.sectionId]) init(config);    
    SD[config.sectionId] = publicAPI;
    
    return publicAPI;
  })


  // ******************************

  function init(config) {
    var tooltips = getTooltips(config);
    var triggers = pluck(tooltips, 'trigger');

    triggers.map(function(element) {
      element.addEventListener('click', function triggerClick() {
        tooltipClick(tooltips, element.value);
      });
    });
  }

  function getConfig(instance) {
    return JSON.parse(instance.innerHTML);
  }

  function getTooltips(config) {
    return config.blocksId.map(getTooltip);
  }

  function getTooltip(id) {
    var host = document.getElementById('tooltip-' + id);

    return {
      id: id,
      trigger: host.querySelector('[data-tooltip-trigger]'),
      overlay: host.querySelector('[data-tooltip-overlay]'),
      markup: host.querySelector('[data-tooltip-markup]'),
      get collapsed() { return isCollapsed(this) }
    }
  }

  function tooltipClick(tooltips, id) {
    var isSelected = partial(idMatch, id);
    tooltips.filter(isSelected).map(toggle);
    tooltips.filter(not(isSelected)).filter(not(isCollapsed)).map(collapse);
  }
  
  function toggle(tooltip) {
    tooltip.collapsed ? expand(tooltip) : collapse(tooltip);
  }

  function expand(tooltip) {
    tooltip.overlay.innerHTML = tooltip.markup.textContent;
    tooltip.overlay.style.setProperty('--end-h', height(tooltip));
    tooltip.overlay.style.setProperty('--start-h', '0px');
    tooltip.trigger.setAttribute('aria-expanded', true);

    tooltip.overlay.addEventListener('animationend', function animationEndFn() {
      animationReset(tooltip.overlay, animationEndFn);
    });

    return tooltip;
  }

  function collapse(tooltip) {
    tooltip.overlay.style.setProperty('--start-h',  height(tooltip));
    tooltip.overlay.style.setProperty('--end-h', '0px');
    tooltip.trigger.setAttribute('aria-expanded', false);
    
    if (!support.customProperties) tooltip.overlay.innerHTML = '';

    tooltip.overlay.addEventListener('animationend', function animationEndFn() {
      animationReset(tooltip.overlay, animationEndFn);
      tooltip.overlay.innerHTML = '';
    });

    return tooltip;
  }

  function animationReset(element, callback) {
    element.removeAttribute('style');
    element.removeEventListener('animationend', callback);
  }

  function idMatch(id, tooltip) {
    return tooltip.id === id;
  }

  function isCollapsed(tooltip) {
    return attr('aria-expanded', tooltip.trigger) === 'false';
  }

  function height(tooltip) {
    return px(tooltip.overlay.offsetHeight.toString());
  }

  function px(n) {
    return n + 'px';
  }

  /* DOM */
  function attr(str, element) {
    return element.getAttribute(str);
  }

  function nodeList(str, root) {
    if (!root) root = document;
    var nodeList = root.querySelectorAll(str);
    return Array.prototype.slice.call(nodeList);
  }

  /* Fn */
  function not(predicate) {
    return function negated() {
      return !predicate.apply(undefined, arguments);
    };
  }

  function pluck(array, key) {
    return array.map(function(obj) {
      return obj[key];
    });
  }

  function partial(fn) {
    var slice = Array.prototype.slice;
    var args = slice.call(arguments, 1);
    return function() {
      return fn.apply(this, args.concat(slice.call(arguments, 0)));
    };
  }

  /* Support */
  function getSupport() {
    return {
      customProperties: window.CSS && CSS.supports('color', 'var(--primary)'),
    }
  }

  /* Theme editor */
  function select(blockId) {
    return expand(getTooltip(blockId));
  }

  function deselect(blockId) {
    return collapse(getTooltip(blockId));
  }
  
})(window.SectionsDesign = window.SectionsDesign || {});


const tooltipButtons = document.querySelectorAll('.tooltip-button');
document.addEventListener('click', function (e) {
  const target = e.target.closest('.tooltip-close');
  if(target){
    tooltipButtons.forEach(function(e){
      e.setAttribute('aria-expanded', 'false');
    });
  } else {
    let isClickInside = false;
    tooltipButtons.forEach(function(button) {
      if(button.contains(e.target)) {
        isClickInside = true;
      }
    });
    if(!isClickInside) {
      tooltipButtons.forEach(function(button){
        button.setAttribute('aria-expanded', 'false');
      });
    }
  }
});

 

 

Once the code is added goto the theme customizer and add section Image Tag where you can upload Background image and dots color then you can add the tag and select the product you want to show with that you can also change the position of the tag on the image. You can add upto 50 tags / hotspot on the image.

HuptechWeb_0-1724937087798.png

HuptechWeb_1-1724938107393.png

 

Thank you

D.P.

If you found this response helpful, please do like and accept the solution. Thanks!
Need support with Customizing your Shopify store?
Feel free to contact me at info@huptechweb.com or Visit our website Huptech Web.
Instant Shortcode Builder: Integrate customizable UI features anywhere in your store - No coding knowledge required

View solution in original post

Reply 1 (1)

Huptech-Web
Shopify Partner
908 186 189

This is an accepted solution.

Hi @rehamessawy 
You can follow the below instructions to make the image hotspot

 

Step 1: Create a section and name it tooltips So once saved, It will be tooltips.liquid

Step 2: Add the below code to the tooltips.liquid

 

{%- comment -%} ---------------- THE CSS --------------------- {%- endcomment -%}

{%- assign button_width_small = 28 -%}
{%- assign button_width_large = 20 -%}
{%- assign tooltip_max_width = 320 -%}
{%- assign image_aspect_ratio = section.settings.image.aspect_ratio | default: 1 -%}
{%- assign section_selector = '[data-tooltips="' | append: section.id | append: '"]'-%}
{%- style -%}
  .section-{{ section.id }}-padding {
    padding-top: {{ section.settings.padding_top | times: 0.5 | round: 0 }}px;
    padding-bottom: {{ section.settings.padding_bottom | times: 0.5 | round: 0 }}px;
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top }}px;
      padding-bottom: {{ section.settings.padding_bottom }}px;
    }
  }
{%- endstyle -%}
<style>
  main
  .tooltips-section {position: relative;}
  .tooltips-figure {margin: 0;}
  main#MainContent {overflow: hidden;}
  .hw-tooltip--productImage {position: relative;width: 18rem;min-width: 18rem;min-height: 27rem;}
  .hw-tooltip--productImage img {position: absolute;width: 100%;height: 100%;inset: 0;object-fit: cover;}
  .hw-tooltip--content .badge {display: none;}
  .hw-tooltip--content .price .price-item {margin: 0;font-size: 1.4rem;font-family: var(--body-textBold);}
  body .hw-tooltip--content .price--on-sale .price-item--regular {font-size: 1.4rem;font-family: var(--body-text);}
  .hw-tooltip--content {display: flex;flex-direction: column;justify-content: center;padding: .5rem;row-gap: .5rem;}
  .hw-tooltip--content .product__title {font-size: 1.4rem;}
  .hw-tooltip--content .price--on-sale .price__sale {display: flex;flex-direction: row-reverse;align-items: center;column-gap: .5rem;}
  .tooltip-close {position: absolute;right: 0;top: 0;width: 4rem;height: 4rem;color: var(--dark-900);cursor: pointer;z-index: 1;display: flex;justify-content: center;align-items: center;}
  .tooltip-close:hover {color: var(--primary-900);}
  .hw-tooltip--content .price .badge {display: none;}
  .hw-tooltip--content .product__title.hw-link.hw-link--arrow {padding-left: 0;margin-top: .5rem;}
  .hw-tooltip--content .product__title > .body-textBold {color: var(--dark-800);line-height: 1.4;}
  .tooltips-img {display: block;width: 100%;}
  .tooltips-list {padding: 0 0 0 32px;list-style: decimal;}
  .tooltip-item {box-sizing: border-box;padding: 8px 12px;}
  .tooltip-button {background: transparent;width: 100%;border: 0;padding: 0;text-align: left;z-index: 1;}
  .tooltip-button:focus {outline: none;}
  .tooltip-index {border-radius: 50%;text-align: center;position: absolute;padding: 0;
   -webkit-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
    line-height: {{ button_width_small }}px;
    width: {{ button_width_small }}px;
    cursor: pointer
  }
  .tooltip-index::before {content: '';position: absolute;width: 1.5rem;height: 1.5rem;border-radius: 100vh;inset: 0;margin: auto;}
 .tooltip-index::after {content: '';position: absolute;width: 1rem;height: 1rem;border-radius: 100vh;inset: 0;margin: auto;animation: pulse-animation 3s infinite;z-index: -1;}
@keyframes pulse-animation {
  0% {
    box-shadow: 0 0 0 0px rgba(154, 33, 31, 0.6);
    
  }
  100% {
    box-shadow: 0 0 0 20px rgba(154, 33, 31, 0);
  }
}
  .tooltip-overlay {background: var(--light-900);opacity: 1;overflow: hidden;will-change: height;}
  .tooltip-header {display: none;}
  
  
  {{ section_selector }} .tooltip-index::before {
    background-color: {{ section.settings.tooltip_background_color }};
  }
  
  {%- for block in section.blocks -%}
  {{ section_selector }} .tooltip-item:nth-child({{ forloop.index }}) .tooltip-index {
    top: 0px;
    margin-top: {{ block.settings.top | divided_by: image_aspect_ratio }}%;
    left: {{ block.settings.left }}%;
  }
  {%- endfor -%}
  
  .tooltip-item .tooltip-overlay:empty {animation: none;}
  
  [aria-expanded="true"] ~ .tooltip-overlay {
    animation: tooltip-expand 200ms both cubic-bezier(0.4, 0, 0.2, 1);
  }
  
  [aria-expanded="false"] ~ .tooltip-overlay {
    animation: tooltip-collapse 180ms both cubic-bezier(0.4, 0, 0.2, 1);
  }
  
  @keyframes tooltip-expand {
    0% { height: var(--start-h) }
    100% { height: var(--end-h) }
  }
  
  @keyframes tooltip-collapse {
    0% { height: var(--start-h) }
    100% { height: var(--end-h) }
  }

  @media (prefers-reduced-motion: reduce) {
    .tooltip-overlay {
      animation-duration: 0s!important;
    }
  }
    .tooltips-list {margin: 0;padding: 0;list-style: none;}
    .tooltip-button .tooltip-index {font-size: 0;color: rgba(0,0,0,0);position: static;transform: translate(0, 0);line-height: 1;height: {{ button_width_large }}px;width: {{ button_width_large }}px;}
    .tooltip-button {position: absolute;padding: 0;font-size: 16px;-webkit-transform: translate(-50%, -50%);transform: translate(-50%, -50%);width: {{ button_width_large }}px;height: {{ button_width_large }}px;}
    .tooltip-title {display: none;}
    .tooltip-item {max-width: {{ tooltip_max_width }}px;padding: 0;}
    [aria-expanded="true"] ~ .tooltip-overlay {margin-left: -{{ button_width_large }}px;z-index: 3;}
    [aria-expanded="true"].tooltip-button {z-index: 4;}
    .tooltip-overlay:empty {padding: 0;opacity: 0;transform: scale(0, 0);}
    .tooltip-overlay {max-width: 41.5rem;padding: 0;border-radius: .5rem;transform-origin: top left;position: absolute;will-change: opacity, transform;display: flex;}
    .tooltip-header {display: block;}
   .hw-tooltip--content .price .price-item {font-size: 1.4rem;}
    [aria-expanded="true"] ~ .tooltip-overlay {animation: tooltip-expand-large 200ms both cubic-bezier(0.4, 0, 0.2, 1);}
    [aria-expanded="false"] ~ .tooltip-overlay {animation: tooltip-collapse-large 180ms both cubic-bezier(0.4, 0, 0.2, 1);}
    
    @keyframes tooltip-expand-large {
      0% { opacity: 0; transform: scale(.2, .2); }
      100% { opacity: 1; transform: scale(1, 1); }
    }
    
    @keyframes tooltip-collapse-large {
      0% { opacity: 1; }
      100% { opacity: 0; }
    }
    
    {%- for block in section.blocks -%}
      {%- assign y = block.settings.top | divided_by: image_aspect_ratio -%}
      {%- assign tooltip_selector = '#tooltip-' | append: block.id -%}

      {{ tooltip_selector }} .tooltip-button {
        top: 0px;
        margin-top: {{ y }}%;
        left: {{ block.settings.left }}%;
      }

      {{ tooltip_selector }} .tooltip-overlay {
        top: calc(-{{ button_width_large }}px + 40px);
        margin-top: {{ y }}%;
        left: {{ block.settings.left }}%;
        {% if block.settings.right != 0 %}
          left: auto;
          right: {{ block.settings.right }}%;
          {% endif %}
      }

      {{ tooltip_selector }} .tooltip-button .tooltip-index {
        margin-top: 0;
      }
    {%- endfor -%}
.hw-tooltip--productImage {background-color: var(--light-800);}
@media screen and (min-width: 990px) {
.tooltip-overlay {min-width: 41.5rem;}
.hw-tooltip--content {flex: 1 1 100%;padding: 1.2rem 1.5rem;}  
}
@media screen and (min-width: 750px) {
.hw-tooltip--content .product__title:not(.hw-link) {font-size: 1.8rem;line-height: 2.8rem;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 3;overflow: hidden;}
.hw-tooltip--content .product__title > .body-textBold {line-height: inherit;}
body .hw-tooltip--content .price--on-sale .price-item--regular {font-size: 1.6rem;}
.hw-tooltip--content .price .price-item {font-size: 1.8rem;}
}  
@media screen and (max-width: 749px) {
  .tooltip-overlay {flex-direction: column;max-width: 18rem;}
  .hw-tooltip--productImage {position: relative;width: 100%;min-width: 100%;min-height: 27rem}
  .hw-tooltip--content {padding: 1.2rem;}
  .tooltip-index::before {width: .8rem;height: .8rem;}
  .tooltip-index::after {width: .5rem;height: .5rem;animation: pulse-animation 2s infinite;}
}  
</style>


{%- comment -%} ---------------- THE NO-JS ------------------- {%- endcomment -%}

<noscript>
  <style>
    .tooltips-section .tooltips-list {list-style: decimal;padding: 24px;}
    .tooltip-item {position: static;padding: 16px;max-width: none;}
  </style>
</noscript>


{%- comment -%} ---------------- THE MARKUP ------------------ {%- endcomment -%}
<div class="section-{{ section.id }}-padding">
<div class="page-width">
<div class="tooltips-section" data-tooltips="{{ section.id }}">
  
  <figure class="tooltips-figure">
    {%- if section.settings.image == blank -%}
      {{ 'image' | placeholder_svg_tag: 'tooltips-picture' }}
    {%- else -%}
    <picture class="tooltips-picture">
      <source srcset="{{ section.settings.image | img_url: '320x' }} 1x,
                      {{ section.settings.image | img_url: '320x', scale: 2 }} 2x" media="(max-width: 320px)">
      <source srcset="{{ section.settings.image | img_url: '420x' }} 1x,
                      {{ section.settings.image | img_url: '420x', scale: 2 }} 2x" media="(max-width: 420px)">
      <source srcset="{{ section.settings.image | img_url: '768x' }} 1x,
                      {{ section.settings.image | img_url: '768x', scale: 2 }} 2x" media="(max-width: 768px)">
      <source srcset="{{ section.settings.image | img_url: '1240x' }} 1x,
                      {{ section.settings.image | img_url: '1240x', scale: 2 }} 2x" media="(max-width: 1240px)">
      <source srcset="{{ section.settings.image | img_url: '1440x' }} 1x,
                      {{ section.settings.image | img_url: '1440x', scale: 2 }} 2x" media="(max-width: 1440px)">
      <source srcset="{{ section.settings.image | img_url: '1660x' }} 1x,
                      {{ section.settings.image | img_url: '1660x', scale: 2 }} 2x" media="(max-width: 1660px)">
      <source srcset="{{ section.settings.image | img_url: '2048x' }} 1x,
                      {{ section.settings.image | img_url: '2048x', scale: 2 }} 2x" media="(min-width: 1661px)">
      <img class="tooltips-img" src="{{ section.settings.image | img_url: '2048x' }}" alt="{{ section.settings.image.alt }}">
    </picture>
    {%- endif -%}
  </figure>
  
  <ol class="tooltips-list" aria-label="{{ section.settings.title }}">
    {%- for block in section.blocks -%}
      {% if block.settings.selected_product %}
        {% assign product = block.settings.selected_product %}
    <li class="tooltip-item" id="tooltip-{{ block.id }}">
      <button class="tooltip-button"
              type="button" 
              value="{{ block.id }}"
              aria-describedby="tooltip-overlay-{{ block.id }}" 
              aria-label="{{ forloop.index }}, {{ block.settings.title }}" 
              aria-expanded="false" 
              data-tooltip-trigger
              {{ block.shopify_attributes }} >
        <div class="tooltip-index" role="none">{{ forloop.index }}</div>
        
        <strong class="tooltip-title" role="none">{{ block.settings.title }}</strong>
      </button>
      <aside class="tooltip-overlay" id="tooltip-overlay-{{ block.id }}" data-tooltip-overlay></aside>
      
      
      
      <noscript data-tooltip-markup>
      <div class="tooltip-close" data-tooltip-close>{% render 'icon-close-large' %}</div>
        <div class="hw-tooltip--productImage"><img width="180" heigh="180" src="{{ product.featured_image | img_url:'180x' }}" /></div>
        <div class="hw-tooltip--content">
        <div class="product__title" {{ block.shopify_attributes }}>
                <div class="body-textBold">{{ product.title | escape }}</div>
              </div>
      <div class="no-js-hidden" id="price-{{ section.id }}" role="status" {{ block.shopify_attributes }}>
                {%- render 'price',
                  product: product,
                  use_variant: true,
                  show_badges: true,
                  price_class: 'price--large'
                -%}
              </div>
        <a {% if block.settings.new_window %}
                          target="_blank"
                        {% endif %} href="{{ product.url }}" class="product__title hw-link hw-link--arrow">
                 
                    {% if block.settings.button_label != blank %}
                      {{ block.settings.button_label }}
                    {% else %}
                      {{ 'products.product.tooltip_button' | t }}
                    {% endif %}
                 
                </a>
        </div>
      </noscript>
        
    </li>
        {% endif %}
    {%- endfor -%}
  </ol>
  
  
{%- comment -%} ---------------- THE CONFIG ------------------ {%- endcomment -%}
  
  <script data-tooltips-config type="application/json">
   {
     "breakpoint": 768,
     "sectionId": {{ section.id | json }},
     "blocksId": {{ section.blocks | map: 'id' | json }}
   }
  </script>

</div>
</div>
</div>
{%- comment -%} ------------------ THE JS -------------------- {%- endcomment -%}

<script src="{{ 'tooltips.js' | asset_url }}" defer="defer"></script>


{%- comment -%} --------------- THEME EDITOR ----------------- {%- endcomment -%}

{%- if section.blocks.last.shopify_attributes != blank -%}
<script>
  (function ThemeEditor(SD){
    var sectionId = {{ section.id | json }};
    if (SD.ThemeEditor[sectionId]) return;
    
    SD.ThemeEditor[sectionId] = 'init';
    initEvents(sectionId);
    
    document.addEventListener('shopify:section:load', function(evt) {
      if (evt.detail.sectionId !== sectionId) return;
      
      var section = SD[sectionId];
      section.init(section.config);
      initEvents(sectionId);
    });

    function initEvents(sectionId) {
      var sectionHost = document.querySelector('[data-tooltips="' + sectionId + '"]');

      sectionHost.addEventListener('shopify:block:select', toggleSelect);
      sectionHost.addEventListener('shopify:block:deselect', toggleSelect); 
    }
    
    function toggleSelect(evt) {
      var sectionId = evt.detail.sectionId;
      var blockId = evt.detail.blockId;
      var section = SD[sectionId];
      
      evt.type === 'shopify:block:select'
      ? section.select(blockId)
      : section.deselect(blockId)
      
      updateBlocks(section, blockId);
    }
    
    function updateBlocks(section, blockId) {
      if (section.config.blocksId.indexOf(blockId) === -1) {
        section.config.blocksId.push(blockId);
      }
    }
 
  })(window.SectionsDesign = window.SectionsDesign || {ThemeEditor: []});

</script>
{%- endif -%}

{%- comment -%} ---------------- THE SETTINGS ---------------- {%- endcomment -%}

{% schema %}
{
  "name": "Image Tag",
  "class": "sd-tooltips",
  "settings": [
    {
      "type": "paragraph",
      "content": "Image tag section"
    },
    {
      "type": "image_picker",
      "id": "image",
      "label": "Background image"
    },
    {
      "type": "color",
      "id": "tooltip_background_color",
      "label": "Dots background color",
      "default": "#64cbdf"
    },
    {
      "type": "header",
      "content": "t:sections.all.padding.section_padding_heading"
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top",
      "default": 0
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_bottom",
      "default": 0
    }
  ],
  "blocks": [
    {
      "type": "Tooltip",
      "name": "Tag",
      "settings": [
        {
          "type": "product",
          "id": "selected_product",
          "label": "Select product to show on this Tag"
        },
        {
          "type": "range",
          "id": "top",
          "min": 0,
          "max": 100,
          "step": 1,
          "unit": "%",
          "label": "Top position",
          "default": 50
        },
        {
          "type": "range",
          "id": "left",
          "min": 0,
          "max": 100,
          "step": 1,
          "unit": "%",
          "label": "Left position",
          "default": 50
        },
         {
          "type": "range",
          "id": "right",
          "min": 0,
          "max": 100,
          "step": 1,
          "unit": "%",
          "label": "Right position",
          "default": 0
        },
        {
          "type": "text",
          "id": "button_label",
          "label": "Button Label"
        },
        {
          "type": "checkbox",
          "id": "new_window",
         "default":false,
          "label": "Open link in new window"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Image Tag",
      "blocks": [
        {
          "type": "Tooltip"
        }
      ]
    }
  ]
}
{% endschema %}

 

 

Step 3: Go to assets and add JS file and name it tooltips So once saved, It will be tooltips.js

Step 4: Add the below code to the tooltips.js

 

 

(function Tooltips(SD){
  'use strict';
  
  var support = getSupport();
  var instances = nodeList('[data-tooltips-config]');

  instances.map(function(instance){
    var config = Object.freeze(getConfig(instance));

    var publicAPI = {
      tooltips: function() { return getTooltips(config) },
      tooltip: getTooltip,
      select: select,
      deselect: deselect,
      config: config,
      init: init
    }

    if (!SD[config.sectionId]) init(config);    
    SD[config.sectionId] = publicAPI;
    
    return publicAPI;
  })


  // ******************************

  function init(config) {
    var tooltips = getTooltips(config);
    var triggers = pluck(tooltips, 'trigger');

    triggers.map(function(element) {
      element.addEventListener('click', function triggerClick() {
        tooltipClick(tooltips, element.value);
      });
    });
  }

  function getConfig(instance) {
    return JSON.parse(instance.innerHTML);
  }

  function getTooltips(config) {
    return config.blocksId.map(getTooltip);
  }

  function getTooltip(id) {
    var host = document.getElementById('tooltip-' + id);

    return {
      id: id,
      trigger: host.querySelector('[data-tooltip-trigger]'),
      overlay: host.querySelector('[data-tooltip-overlay]'),
      markup: host.querySelector('[data-tooltip-markup]'),
      get collapsed() { return isCollapsed(this) }
    }
  }

  function tooltipClick(tooltips, id) {
    var isSelected = partial(idMatch, id);
    tooltips.filter(isSelected).map(toggle);
    tooltips.filter(not(isSelected)).filter(not(isCollapsed)).map(collapse);
  }
  
  function toggle(tooltip) {
    tooltip.collapsed ? expand(tooltip) : collapse(tooltip);
  }

  function expand(tooltip) {
    tooltip.overlay.innerHTML = tooltip.markup.textContent;
    tooltip.overlay.style.setProperty('--end-h', height(tooltip));
    tooltip.overlay.style.setProperty('--start-h', '0px');
    tooltip.trigger.setAttribute('aria-expanded', true);

    tooltip.overlay.addEventListener('animationend', function animationEndFn() {
      animationReset(tooltip.overlay, animationEndFn);
    });

    return tooltip;
  }

  function collapse(tooltip) {
    tooltip.overlay.style.setProperty('--start-h',  height(tooltip));
    tooltip.overlay.style.setProperty('--end-h', '0px');
    tooltip.trigger.setAttribute('aria-expanded', false);
    
    if (!support.customProperties) tooltip.overlay.innerHTML = '';

    tooltip.overlay.addEventListener('animationend', function animationEndFn() {
      animationReset(tooltip.overlay, animationEndFn);
      tooltip.overlay.innerHTML = '';
    });

    return tooltip;
  }

  function animationReset(element, callback) {
    element.removeAttribute('style');
    element.removeEventListener('animationend', callback);
  }

  function idMatch(id, tooltip) {
    return tooltip.id === id;
  }

  function isCollapsed(tooltip) {
    return attr('aria-expanded', tooltip.trigger) === 'false';
  }

  function height(tooltip) {
    return px(tooltip.overlay.offsetHeight.toString());
  }

  function px(n) {
    return n + 'px';
  }

  /* DOM */
  function attr(str, element) {
    return element.getAttribute(str);
  }

  function nodeList(str, root) {
    if (!root) root = document;
    var nodeList = root.querySelectorAll(str);
    return Array.prototype.slice.call(nodeList);
  }

  /* Fn */
  function not(predicate) {
    return function negated() {
      return !predicate.apply(undefined, arguments);
    };
  }

  function pluck(array, key) {
    return array.map(function(obj) {
      return obj[key];
    });
  }

  function partial(fn) {
    var slice = Array.prototype.slice;
    var args = slice.call(arguments, 1);
    return function() {
      return fn.apply(this, args.concat(slice.call(arguments, 0)));
    };
  }

  /* Support */
  function getSupport() {
    return {
      customProperties: window.CSS && CSS.supports('color', 'var(--primary)'),
    }
  }

  /* Theme editor */
  function select(blockId) {
    return expand(getTooltip(blockId));
  }

  function deselect(blockId) {
    return collapse(getTooltip(blockId));
  }
  
})(window.SectionsDesign = window.SectionsDesign || {});


const tooltipButtons = document.querySelectorAll('.tooltip-button');
document.addEventListener('click', function (e) {
  const target = e.target.closest('.tooltip-close');
  if(target){
    tooltipButtons.forEach(function(e){
      e.setAttribute('aria-expanded', 'false');
    });
  } else {
    let isClickInside = false;
    tooltipButtons.forEach(function(button) {
      if(button.contains(e.target)) {
        isClickInside = true;
      }
    });
    if(!isClickInside) {
      tooltipButtons.forEach(function(button){
        button.setAttribute('aria-expanded', 'false');
      });
    }
  }
});

 

 

Once the code is added goto the theme customizer and add section Image Tag where you can upload Background image and dots color then you can add the tag and select the product you want to show with that you can also change the position of the tag on the image. You can add upto 50 tags / hotspot on the image.

HuptechWeb_0-1724937087798.png

HuptechWeb_1-1724938107393.png

 

Thank you

D.P.

If you found this response helpful, please do like and accept the solution. Thanks!
Need support with Customizing your Shopify store?
Feel free to contact me at info@huptechweb.com or Visit our website Huptech Web.
Instant Shortcode Builder: Integrate customizable UI features anywhere in your store - No coding knowledge required