# Image Hot Spots Widget

> Image with positioned interactive hotspot markers. Each hotspot is a Repeater row with x/y coordinates, tooltip, content. Optional Animate.css entrance effects.

**Class file:** `includes/Elements/Image_Hot_Spots.php` (1,094 lines)
**Slug:** `image-hotspots` (widget id `eael-image-hotspots`)
**Public docs:** <https://essential-addons.com/elementor/docs/image-hotspots/>
**Pro-shared:** Pro-only. Vendor: Animate.css (Pro-bundled) + DOMPurify (Lite-shared).

## Overview

Single image with overlaid hotspot markers. Each hotspot has absolute x/y position (% of image), trigger style (dot / icon / image), tooltip text, optional content panel. Animate.css for hotspot entrance animations. DOMPurify for sanitizing user-supplied tooltip HTML content.

## Pro vs Lite

Pro-only.

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Image_Hot_Spots.php` | Widget class (1,094 lines) |
| `src/css/view/image-hotspots.scss` → `assets/front-end/css/view/image-hotspots.min.css` | Styling |
| `src/js/view/image-hotspots.js` → `assets/front-end/js/view/image-hotspots.min.js` | Tooltip / content init |
| `EAEL_PRO_PLUGIN_PATH/assets/front-end/css/lib-view/animate/animate.min.css` | Pro — Animate.css |
| `EAEL_PLUGIN_PATH/assets/front-end/js/lib-view/dom-purify/purify.min.js` | Lite — DOMPurify |
| `config.php` entry `'image-hotspots'` | Mixed libs + Pro CSS + Pro JS |

## Architecture

- **DOMPurify from Lite** — sanitizes user-supplied tooltip HTML at render. Critical XSS defense; relies on Lite's bundled copy.
- **Animate.css for entrance animations** — `lib-view/animate/animate.min.css`.
- **Composes Lite's `Helper`** (line 12).
- **Repeater of hotspots** — each row: position (x%, y%), trigger type, tooltip text, content HTML.
- **Per-hotspot Animate.css class** — applied to marker on enter via JS observation.

## Render Output

```html
<div class="eael-image-hotspots">
  <img src="{base-image}" />
  <!-- Per-hotspot marker positioned absolutely -->
  <div class="eael-hotspot eael-hotspot-{n}"
       style="left:{x}%; top:{y}%"
       data-tooltip="{tooltip}"
       data-content="{sanitized-html-content}"
       data-animation="{animate.css-class}">
    [?] <span class="dot"></span>
    [?] <i class="{icon}"></i>
    [?] <img src="{custom-marker}" />
  </div>
  ...
</div>
```

## Controls Reference

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| Base image | Content → Image | MEDIA | Background image |
| Hotspots Repeater | Content → Hotspots | REPEATER | Per-marker config |
| (Per-marker) x / y position | Inside Repeater | SLIDER | 0–100% |
| (Per-marker) trigger style | Inside Repeater | CHOOSE | `dot` / `icon` / `image` |
| (Per-marker) tooltip text | Inside Repeater | TEXT | Hover tooltip |
| (Per-marker) content HTML | Inside Repeater | WYSIWYG | Click-panel content (DOMPurify-sanitized) |
| (Per-marker) animation | Inside Repeater | SELECT | Animate.css class |
| Marker / tooltip / content styling | Style → ... | various | Per-region styling |

## Conditional Dependencies

```text
trigger = 'icon' → ICONS picker
trigger = 'image' → MEDIA picker (custom marker image)
content set → click-panel render path
```

## JavaScript Lifecycle

Tooltip on hover (CSS or JS), content panel on click. DOMPurify sanitizes content HTML before insertion.

## Hooks & Filters

Standard widget render.

## Common Issues

| Symptom | Cause | Fix |
| --- | --- | --- |
| Hotspot misplaced on responsive | Absolute % position relative to image bounds; padding shifts it | Verify image container has zero padding |
| Tooltip cut off at edge | No edge-detection for tooltip flip | Move hotspot inward |
| Content HTML stripped | DOMPurify removed disallowed tags | Verify allowed tags; avoid `<script>` / `<iframe>` |
| Animation runs in editor | `isEditMode` not gated | Add gate to JS |

## Known Limitations

- **DOMPurify from Lite** — Pro non-functional if Lite removed
- **No keyboard accessibility** for hotspots — `tabindex` + `aria-*` would help screen readers
- **`prefers-reduced-motion` not honoured** for entrance animations
- **No mobile-tap-vs-hover handling** — tooltip-on-hover doesn't apply to touch; rely on tap-open
- **Absolute % positioning fragile** under aspect-ratio shifts (responsive cropping)

## Cross-References

- Sibling: [`img-comparison.md`](img-comparison.md), [`image-scroller.md`](image-scroller.md), [`lightbox.md`](lightbox.md)
- Shared patterns: [`_patterns.md`](_patterns.md)
