All things Shopify and commerce
I 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.
Solved! Go to the solution
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.
Thank you
D.P.
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.
Thank you
D.P.
By investing 30 minutes of your time, you can unlock the potential for increased sales,...
By Jacqui Sep 11, 2024We appreciate the diverse ways you participate in and engage with the Shopify Communi...
By JasonH Sep 9, 2024Thanks to everyone who participated in our AMA with 2H Media: Marketing Your Shopify St...
By Jacqui Sep 6, 2024