# Image Comparison Widget

> Before/after image slider via TwentyTwenty.js. User drags a handle horizontally or vertically to reveal one image atop another.

**Class file:** `includes/Elements/Image_Comparison.php` (1,339 lines)
**Slug:** `img-comparison` (widget id `eael-img-comparison`) ⚠ slug uses `img-` not `image-`
**Public docs:** <https://essential-addons.com/elementor/docs/image-comparison/>
**Pro-shared:** Pro-only. Vendor: TwentyTwenty (from Lite).

## Overview

Side-by-side image comparison. Two images stack; user drags a divider to reveal the underlying image. Used for before/after photo demos, design comparisons, A/B visuals. Pro uses **TwentyTwenty.js (jQuery plugin)** + **imagesLoaded (`pkgd.min.js`)** from Lite's `lib-view/`.

## Pro vs Lite

Pro-only widget. Vendor libs (TwentyTwenty + imagesLoaded) from **Lite**.

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Image_Comparison.php` | Widget class (1,339 lines) |
| `src/css/view/img-comparison.scss` → `assets/front-end/css/view/img-comparison.min.css` | Pro styling |
| `src/js/view/img-comparison.js` → `assets/front-end/js/view/img-comparison.min.js` | Init handler |
| `EAEL_PLUGIN_PATH/assets/front-end/css/lib-view/twentytwenty/twentytwenty.min.css` | Lite — TwentyTwenty CSS |
| `EAEL_PLUGIN_PATH/assets/front-end/js/lib-view/imagesloaded/imagesloaded.pkgd.min.js` | Lite — imagesLoaded |
| `EAEL_PLUGIN_PATH/assets/front-end/js/lib-view/twentytwenty/...` | Lite — TwentyTwenty JS |
| `config.php` entry `'img-comparison'` | Lite libs + Pro CSS + Pro JS |

## Architecture

- **TwentyTwenty + imagesLoaded both from Lite** — Pro depends on Lite for both vendor libs. Failure mode: Lite missing → widget no-op.
- **Composes Lite's `Helper`** (line 14).
- **Orientation toggle** — `eael_image_comp_orientation` (line 297) — `horizontal` / `vertical` divider drag.
- **Before/after labels** — `before_image_label` / `after_image_label` overlay text.
- **Per-image alt text** — accessibility-aware: `before_image_alt` / `after_image_alt` (lines 189, 247).
- **`Group_Control_Image_Size` for before image** (line 269) — Pro respects WP image-size tokens.

## Render Output

```html
<div class="eael-img-comparison eael-img-comparison-{horizontal|vertical}">
  <div class="twentytwenty-container">
    <img class="before" src="{before_url}" alt="{before_alt}" />
    <img class="after" src="{after_url}" alt="{after_alt}" />
  </div>
  [?] <span class="eael-before-label">{before_image_label}</span>
  [?] <span class="eael-after-label">{after_image_label}</span>
</div>
```

After TwentyTwenty init, the container gets `.twentytwenty-overlay`, `.twentytwenty-handle`, etc.

## Controls Reference

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| `before_image` / `after_image` | Content → Images | MEDIA | Two images to compare |
| `before_image_label` / `after_image_label` | Content → Labels | TEXT | Overlay labels |
| `before_image_alt` / `after_image_alt` | Content → A11y | TEXT | Per-image alt text |
| `eael_image_comp_orientation` | Content → Settings | CHOOSE | `horizontal` / `vertical` |
| Default handle position | Content → Settings | SLIDER | Initial divider position (%) |
| `Group_Control_Image_Size` (`eael_before_image_size`) | Content → Images | GROUP | WP image-size for before image |

## Conditional Dependencies

```text
orientation = 'horizontal' → horizontal divider styling
orientation = 'vertical'   → vertical divider styling
labels_show = 'yes' → label TEXT + style controls visible
```

## JavaScript Lifecycle

`src/js/view/img-comparison.js`:

```js
var ImgCompareHandler = function( $scope, $ ) {
    var $container = $scope.find( '.twentytwenty-container' );
    $container.imagesLoaded( function() {
        $container.twentytwenty( {
            orientation: $container.data( 'orientation' ),
            default_offset_pct: $container.data( 'offset' )
        } );
    } );
};
```

Waits for images to load before triggering TwentyTwenty — prevents broken initial render.

## Hooks & Filters

Standard widget render.

## Common Issues

| Symptom | Cause | Fix |
| --- | --- | --- |
| Divider missing | TwentyTwenty not loaded (Lite deactivated) | Verify Lite active |
| Images different aspect ratios | TwentyTwenty assumes matching dimensions | Crop both to same dimensions before upload |
| Wrong default offset | `default_offset_pct` not applied | Inspect data attribute on container |
| Labels overlap on small screens | No responsive offset | Use responsive controls |

## Known Limitations

- **Both images must have matching dimensions** for clean comparison
- **TwentyTwenty unmaintained upstream**
- **Dependent on Lite** for both vendor libs
- **No animated mode** (some libs auto-animate the handle on load; not implemented)
- **No keyboard accessibility** for divider drag

## Cross-References

- Sibling display widgets: [`image-hotspots.md`](image-hotspots.md), [`image-scroller.md`](image-scroller.md), [`lightbox.md`](lightbox.md)
- Shared patterns: [`_patterns.md`](_patterns.md)
