What's your biggest current challenge? Have your say in Community Polls along the right column.

Code help with Hotspots and Tooltips

Solved

Code help with Hotspots and Tooltips

dalemitchley
Shopify Partner
11 0 1

Hi All,

 

Im hoping someone can help with  this issue, Im no developer and have some understanding of code. 

 

Im setting  an image 2/3 of the page with and have a table to the right of it in the other  1/3.

The image is set at 1000px X 1000px, Im then adding a tooltip/hotspot over the image, Ive then setup a selector on that so you can select a product add your qty and add it to cart. This all works, my issue is adding the tooltips/hotspot onto the image, once its been added and I view the page on the frontend its not keeping the tooltip/hotspot in the correct position. I need to lock the position in the theme editor so it does not move.

 

Any help would be appreciated. Thanks in Advance

 

Here is the code Ive put together.

 

{% schema %}
{
"name": "Hotspot v6.1",
"settings": [
{
"type": "image_picker",
"id": "image",
"label": "Image"
}
],
"blocks": [
{
"type": "product_block",
"name": "Product Block",
"settings": [
{
"type": "product",
"id": "product",
"label": "Select Product"
},
{
"type": "range",
"id": "x_position",
"label": "X Position",
"min": 0,
"max": 100,
"default": 50
},
{
"type": "range",
"id": "y_position",
"label": "Y Position",
"min": 0,
"max": 100,
"default": 50
}
]
}
],
"presets": [
{
"name": "Hotspot v6.1",
"category": "Custom"
}
]
}
{% endschema %}

<div class="custom-image-table-section">
{% if section.settings.image %}
<div class="image-block">
<img
src="{{ section.settings.image | img_url: '1000x1000' }}"
alt="Custom Image"
width="1000"
height="1000"
style="width: 100%; height: auto;"
>

{% assign displayed_products = '' %}

{% for block in section.blocks %}
{% if block.settings.product %}
{% if block.settings.product.variants.size > 0 %}
{% assign product_sku = block.settings.product.variants.first.sku %}
{% assign product_price = block.settings.product.variants.first.price | money %}
{% else %}
{% assign product_sku = 'No SKU' %}
{% assign product_price = 'Price unavailable' %}
{% endif %}
{% unless displayed_products contains product_sku %}
{% assign displayed_products = displayed_products | append: product_sku | append: ',' %}
<div
class="tooltip"
style="top: {{ block.settings.y_position }}%; left: {{ block.settings.x_position }}%;"
data-sku="{{ product_sku }}"
>
<span class="tooltiptext">
{{ block.settings.product.title }}
<br>
{{ product_price }}
</span>
</div>
{% endunless %}
{% endif %}
{% endfor %}
</div>
{% endif %}

<div class="table-block">
<h2>Parts Information</h2>
<div class="table-container">
<table class="responsive-table">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>
<div class="cart-icon"><span id="cart-icon-count">0</span>🛒</div>
</th>
</tr>
</thead>
<tbody>
{% assign displayed_products = '' %}

{% for block in section.blocks %}
{% if block.settings.product %}
{% if block.settings.product.variants.size > 0 %}
{% assign product_sku = block.settings.product.variants.first.sku %}
{% assign product_price = block.settings.product.variants.first.price | money %}
{% else %}
{% assign product_sku = 'No SKU' %}
{% assign product_price = 'Price unavailable' %}
{% endif %}
{% unless displayed_products contains product_sku %}
{% assign displayed_products = displayed_products | append: product_sku | append: ',' %}
<tr class="product-row" data-sku="{{ product_sku }}">
<td>Ref: {{ forloop.index }}</td>
<td>
Sku: <b>{{ product_sku }}</b> <br>
{{ block.settings.product.title }}
</td>
<td>
Price: {{ product_price }}
<br>
<form class="add-to-cart-form" method="post">
{% if block.settings.product %}
<input type="hidden" name="id" value="{{ block.settings.product.variants.first.id }}">
<input type="number" name="quantity" id="quantity" value="1" min="1" style="width: 50px;">
<button type="submit" class="add-to-cart-btn">Add to Cart</button>
{% else %}
<p>Product not found.</p>
{% endif %}
</form>
<div class="success-message" style="display: none;">Item added to cart!</div>
</td>
</tr>
{% endunless %}
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>

<style>
.custom-image-table-section {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 20px;
}

.image-block {
position: relative;
flex: 1;
max-width: 50%;
}

.table-block {
flex: 1;
margin-bottom: 20px;
max-width: 50%;
}

.tooltip {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border: 3px solid #555;
border-radius: 50%;
cursor: pointer;
background-color: transparent;
color: transparent;
transform: translate(-50%, -50%);
top: 0;
left: 0;
}

.tooltip:hover {
border: 4px solid #D93939;
}

.tooltip .tooltiptext {
visibility: hidden;
width: 10vw;
max-width: 140px;
background-color: #333;
color: #fff;
font-size: 12px;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
}

.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}

.highlight {
background-color: #e88888;
}

@media (max-width: 768px) {
.custom-image-table-section {
flex-direction: column;
}
.image-block, .table-block {
max-width: 100%;
}
}
</style>

<script>
document.addEventListener("DOMContentLoaded", function() {

const tooltips = document.querySelectorAll('.tooltip');
const rows = document.querySelectorAll('.product-row');


tooltips.forEach((tooltip) => {
tooltip.addEventListener('click', () => {
const productSku = tooltip.getAttribute('data-sku');
highlightRowBySku(productSku);
});
});


function highlightRowBySku(productSku) {
rows.forEach(row => {
const rowSku = row.getAttribute('data-sku');
if (rowSku === productSku) {
row.classList.add('highlight');
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
} else {
row.classList.remove('highlight');
}
});
}


document.querySelectorAll('.add-to-cart-form').forEach(form => {
form.addEventListener('submit', function(event) {
event.preventDefault();

const quantityInput = this.querySelector('input[name="quantity"]');
const quantity = parseInt(quantityInput.value, 10);
if (isNaN(quantity) || quantity < 1) {
alert('Please enter a valid quantity.');
return;
}

const button = this.querySelector('.add-to-cart-btn');
button.disabled = true;
button.textContent = 'Adding...';

const formData = new FormData(this);

fetch('/cart/add.js', {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
}
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return fetch('/cart.js');
})
.then(response => response.json())
.then(cartData => {
const cartCountElement = document.getElementById('cart-icon-count');
if (cartCountElement) {
cartCountElement.textContent = cartData.item_count;
}
const successMessage = this.querySelector('.success-message');
if (successMessage) {
successMessage.style.display = 'block';
setTimeout(() => {
successMessage.style.display = 'none';
}, 3000);
}
})
.catch(error => {
console.error('Error:', error);
alert('There was an issue adding the item to the cart. Please try again.');
})
.finally(() => {
button.disabled = false;
button.textContent = 'Add to Cart';
});
});
});
});
</script>

 

Accepted Solution (1)

TheUntechnickle
Shopify Partner
101 10 51

This is an accepted solution.

Hi @dalemitchley ,

Thanks for reaching out about the hotspot positioning issue. I've reviewed your code and I understand the problem with the tooltips not staying in their correct positions. I have a solution that should fix this for you.

The main issue is with how the image container and tooltips are being positioned. I've made some improvements to the code that will keep the hotspots locked in place, while maintaining your 2/3 and 1/3 layout for the image and table.

Here are the key changes I've made:

  1. Added a proper container structure for the image to maintain aspect ratio
  2. Improved the tooltip positioning system using CSS custom properties
  3. Fixed the section layout to ensure the correct 2/3 to 1/3 ratio
  4. Enhanced the responsive behavior for mobile devices

I've attached the complete updated code below. To implement this fix:

  1. Replace your current section code with this new version
  2. Keep your existing schema and JavaScript code as they are
  3. Test the positioning in the theme editor



 

{%- comment -%}
The schema remains the same as your original code
{%- endcomment -%}
{% schema %}
{
  "name": "Hotspot v6.1",
  "settings": [
    {
      "type": "image_picker",
      "id": "image",
      "label": "Image"
    }
  ],
  "blocks": [
    {
      "type": "product_block",
      "name": "Product Block",
      "settings": [
        {
          "type": "product",
          "id": "product",
          "label": "Select Product"
        },
        {
          "type": "range",
          "id": "x_position",
          "label": "X Position",
          "min": 0,
          "max": 100,
          "default": 50
        },
        {
          "type": "range",
          "id": "y_position",
          "label": "Y Position",
          "min": 0,
          "max": 100,
          "default": 50
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Hotspot v6.1",
      "category": "Custom"
    }
  ]
}
{% endschema %}

<div class="custom-image-table-section">
  {% if section.settings.image %}
    <div class="image-block">
      <div class="image-container">
        <img
          src="{{ section.settings.image | img_url: '1000x1000' }}"
          alt="Custom Image"
          width="1000"
          height="1000"
          class="product-image"
        >

        {% assign displayed_products = '' %}
        {% for block in section.blocks %}
          {% if block.settings.product %}
            {% if block.settings.product.variants.size > 0 %}
              {% assign product_sku = block.settings.product.variants.first.sku %}
              {% assign product_price = block.settings.product.variants.first.price | money %}
            {% else %}
              {% assign product_sku = 'No SKU' %}
              {% assign product_price = 'Price unavailable' %}
            {% endif %}
            
            {% unless displayed_products contains product_sku %}
              {% assign displayed_products = displayed_products | append: product_sku | append: ',' %}
              <div
                class="tooltip"
                style="--x-pos: {{ block.settings.x_position }}%; --y-pos: {{ block.settings.y_position }}%;"
                data-sku="{{ product_sku }}"
              >
                <span class="tooltiptext">
                  {{ block.settings.product.title }}
                  <br>
                  {{ product_price }}
                </span>
              </div>
            {% endunless %}
          {% endif %}
        {% endfor %}
      </div>
    </div>
  {% endif %}

  {%- comment -%}Rest of your table block code remains the same{%- endcomment -%}
  <div class="table-block">
    <!-- Your existing table code -->
  </div>
</div>

<style>
.custom-image-table-section {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  gap: 20px;
  width: 100%;
}

.image-block {
  flex: 2; /* Changed from 1 to 2 to make it take up 2/3 of the space */
  max-width: 66.666%; /* 2/3 of the width */
}

.table-block {
  flex: 1;
  margin-bottom: 20px;
  max-width: 33.333%; /* 1/3 of the width */
}

.image-container {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 100%; /* Creates a square aspect ratio */
}

.product-image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.tooltip {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 3px solid #555;
  border-radius: 50%;
  cursor: pointer;
  background-color: transparent;
  color: transparent;
  top: var(--y-pos);
  left: var(--x-pos);
  transform: translate(-50%, -50%);
  z-index: 2;
}

.tooltip:hover {
  border: 4px solid #D93939;
}

.tooltip .tooltiptext {
  visibility: hidden;
  width: 140px;
  background-color: #333;
  color: #fff;
  font-size: 12px;
  text-align: center;
  border-radius: 6px;
  padding: 5px;
  position: absolute;
  z-index: 3;
  bottom: 125%;
  left: 50%;
  margin-left: -70px;
  opacity: 0;
  transition: opacity 0.3s;
}

.tooltip:hover .tooltiptext {
  visibility: visible;
  opacity: 1;
}

.highlight {
  background-color: #e88888;
}

@media (max-width: 768px) {
  .custom-image-table-section {
    flex-direction: column;
  }
  
  .image-block,
  .table-block {
    max-width: 100%;
  }
  
  .tooltip .tooltiptext {
    width: 120px;
    margin-left: -60px;
  }
}
</style>

{%- comment -%}Your existing JavaScript remains the same{%- endcomment -%}

 



The tooltips should now stay perfectly positioned on your image, even when:

  • The browser window is resized
  • Viewing on different screen sizes
  • Making adjustments in the theme editor

 

Let me know if you need any help implementing these changes or if you have any questions. I'm happy to clarify anything that's not clear.

 

Best regards,
Shubham | Untechnickle

Oh, and there’s something brewing behind the scenes that’s redefining customer experience. Curious? It’s time to Revize your store. Don’t say we didn’t warn you. 

View solution in original post

Replies 2 (2)

TheUntechnickle
Shopify Partner
101 10 51

This is an accepted solution.

Hi @dalemitchley ,

Thanks for reaching out about the hotspot positioning issue. I've reviewed your code and I understand the problem with the tooltips not staying in their correct positions. I have a solution that should fix this for you.

The main issue is with how the image container and tooltips are being positioned. I've made some improvements to the code that will keep the hotspots locked in place, while maintaining your 2/3 and 1/3 layout for the image and table.

Here are the key changes I've made:

  1. Added a proper container structure for the image to maintain aspect ratio
  2. Improved the tooltip positioning system using CSS custom properties
  3. Fixed the section layout to ensure the correct 2/3 to 1/3 ratio
  4. Enhanced the responsive behavior for mobile devices

I've attached the complete updated code below. To implement this fix:

  1. Replace your current section code with this new version
  2. Keep your existing schema and JavaScript code as they are
  3. Test the positioning in the theme editor



 

{%- comment -%}
The schema remains the same as your original code
{%- endcomment -%}
{% schema %}
{
  "name": "Hotspot v6.1",
  "settings": [
    {
      "type": "image_picker",
      "id": "image",
      "label": "Image"
    }
  ],
  "blocks": [
    {
      "type": "product_block",
      "name": "Product Block",
      "settings": [
        {
          "type": "product",
          "id": "product",
          "label": "Select Product"
        },
        {
          "type": "range",
          "id": "x_position",
          "label": "X Position",
          "min": 0,
          "max": 100,
          "default": 50
        },
        {
          "type": "range",
          "id": "y_position",
          "label": "Y Position",
          "min": 0,
          "max": 100,
          "default": 50
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Hotspot v6.1",
      "category": "Custom"
    }
  ]
}
{% endschema %}

<div class="custom-image-table-section">
  {% if section.settings.image %}
    <div class="image-block">
      <div class="image-container">
        <img
          src="{{ section.settings.image | img_url: '1000x1000' }}"
          alt="Custom Image"
          width="1000"
          height="1000"
          class="product-image"
        >

        {% assign displayed_products = '' %}
        {% for block in section.blocks %}
          {% if block.settings.product %}
            {% if block.settings.product.variants.size > 0 %}
              {% assign product_sku = block.settings.product.variants.first.sku %}
              {% assign product_price = block.settings.product.variants.first.price | money %}
            {% else %}
              {% assign product_sku = 'No SKU' %}
              {% assign product_price = 'Price unavailable' %}
            {% endif %}
            
            {% unless displayed_products contains product_sku %}
              {% assign displayed_products = displayed_products | append: product_sku | append: ',' %}
              <div
                class="tooltip"
                style="--x-pos: {{ block.settings.x_position }}%; --y-pos: {{ block.settings.y_position }}%;"
                data-sku="{{ product_sku }}"
              >
                <span class="tooltiptext">
                  {{ block.settings.product.title }}
                  <br>
                  {{ product_price }}
                </span>
              </div>
            {% endunless %}
          {% endif %}
        {% endfor %}
      </div>
    </div>
  {% endif %}

  {%- comment -%}Rest of your table block code remains the same{%- endcomment -%}
  <div class="table-block">
    <!-- Your existing table code -->
  </div>
</div>

<style>
.custom-image-table-section {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  gap: 20px;
  width: 100%;
}

.image-block {
  flex: 2; /* Changed from 1 to 2 to make it take up 2/3 of the space */
  max-width: 66.666%; /* 2/3 of the width */
}

.table-block {
  flex: 1;
  margin-bottom: 20px;
  max-width: 33.333%; /* 1/3 of the width */
}

.image-container {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 100%; /* Creates a square aspect ratio */
}

.product-image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.tooltip {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 3px solid #555;
  border-radius: 50%;
  cursor: pointer;
  background-color: transparent;
  color: transparent;
  top: var(--y-pos);
  left: var(--x-pos);
  transform: translate(-50%, -50%);
  z-index: 2;
}

.tooltip:hover {
  border: 4px solid #D93939;
}

.tooltip .tooltiptext {
  visibility: hidden;
  width: 140px;
  background-color: #333;
  color: #fff;
  font-size: 12px;
  text-align: center;
  border-radius: 6px;
  padding: 5px;
  position: absolute;
  z-index: 3;
  bottom: 125%;
  left: 50%;
  margin-left: -70px;
  opacity: 0;
  transition: opacity 0.3s;
}

.tooltip:hover .tooltiptext {
  visibility: visible;
  opacity: 1;
}

.highlight {
  background-color: #e88888;
}

@media (max-width: 768px) {
  .custom-image-table-section {
    flex-direction: column;
  }
  
  .image-block,
  .table-block {
    max-width: 100%;
  }
  
  .tooltip .tooltiptext {
    width: 120px;
    margin-left: -60px;
  }
}
</style>

{%- comment -%}Your existing JavaScript remains the same{%- endcomment -%}

 



The tooltips should now stay perfectly positioned on your image, even when:

  • The browser window is resized
  • Viewing on different screen sizes
  • Making adjustments in the theme editor

 

Let me know if you need any help implementing these changes or if you have any questions. I'm happy to clarify anything that's not clear.

 

Best regards,
Shubham | Untechnickle

Oh, and there’s something brewing behind the scenes that’s redefining customer experience. Curious? It’s time to Revize your store. Don’t say we didn’t warn you. 

dalemitchley
Shopify Partner
11 0 1

@TheUntechnickle, this is great thanks so much for your help works great.