Before/After effect in Javascript using jQuery

Sometimes you need a simple tool to visually show the difference between two images and a basic jQuery plugin is quite suited for that (if you can stomach ~89KB just for jQuery, ~30KB if you gzip it but you probably already have it on your website, especially if you’re running Wordpress ). There are better ways to implement this, someone might suggest React or Vue or Angular or assembler or running it on the blockchain or in the bootloader but this is how I did it.

A jQuery-less version would be quite easy to implement too, perhaps in a future post.

Demo

Check out the demo below, using two images from the 2020 Beirut port explosion .

Code

The actual code is split into three sections, one for HTML (where we inject the container and the two images into our page), one for Javascript (the actual plugin code) and the final one for the CSS styles.

HTML

Our code is depending on the jQuery library so we have to add it to our page. Download it from the official page (please don’t use a CDN and link it from there), extract the archive on your computer and copy the jquery-3.5.1.min.js file. Add the HTML below to your page’s head section (and adjust the URLs, of course).

<link rel="stylesheet" href="/css/before-after.css">
<script src="/js/jquery-3.5.1.min.js"></script>
<script src="/js/before-after.js"></script>

Also we need to add the two images into a container (and remember to use image alt-text if you want captions).

<div class="gallery">
    <img alt="After caption" src="/img/image1.jpg" />
    <img alt="Before caption" src="/img/image2.jpg" />
</div>

Javascript (before-after.js)

The code is quite commented so read it for more information.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
(function($) {
	$.fn.extend({
		before_after: function(options) {
			/**
			 * The default settings, can be changed by passing different ones to
			 * the before_after() function.
			 */
			let default_settings = {
				gap: 50,
				left_gap: 10,
				right_gap: 10,
				caption: false,
				reveal: 0.5
			};
			let settings = $.extend(default_settings, options);
			return this.each(function() {
				/**
				 * Get the width of the first image.
				 */
				let width = $(this).children('img:eq(0)').width();
				/**
				 * Get the height of the first image.
				 */
				let height = $(this).children('img:eq(0)').height();
				/**
				 * Hide the img DOM elements (because we're going to reconstruct
				 * them in divs).
				 */
				$(this).children('img').hide();
				$(this).css({
					overflow: 'hidden',
					position: 'relative'
				});
				/**
				 * Add the mask div element (the after image).
				 */
				$(this).append('<div class="ba-mask"></div>');
				/**
				 * Add the background div element (the before image).
				 */
				$(this).append('<div class="ba-bg"></div>');
				/**
				 * Add the caption div element (the alt-text on the images).
				 */
				$(this).append('<div class="ba-caption">' +
						$(this).children('img:eq(0)').attr('alt') +
					'</div>');
				/**
				 * Set the width and height of the new divs to match the parent
				 * element.
				 */
				$(this).children('.ba-mask, .ba-bg').width(width);
				$(this).children('.ba-mask, .ba-bg').height(height);
				/**
				 * Use a nice animation effect to reveal a bit of the before image.
				 */
				$(this).children('.ba-mask').animate({
					width: width - settings.gap
				}, 1000);
				/**
				 * Set the background images of the two divs.
				 */
				$(this).children('.ba-mask').css('backgroundImage',
					'url(' + $(this).children('img:eq(0)').attr('src') + ')');
				$(this).children('.ba-bg').css('backgroundImage',
					'url(' + $(this).children('img:eq(1)').attr('src') + ')');
				/**
				 * If we want a caption, show it.
				 */
				if (settings.caption) {
					$(this).children('.ba-caption').show();
				}
			}).mousemove(function(c) {
				/**
				 * Get the `left` positioning of the container.
				 */
				let pos_img = $(this).position()['left'];
				/**
				 * Get the `left` positioning of the mouse cursor and substract the
				 * `pos_img` variable from it.
				 */
				let pos_mouse = c.pageX -
					$(this).children('.ba-mask').offset().left;
				let diff = pos_mouse - pos_img;
				/**
				 * Get the width of the container.
				 */
				let width = $(this).width();
				/**
				 * Get the captions (from the alt-texts) of the images.
				 */
				let after_caption = $(this).children('img:eq(0)').attr('alt');
				let before_caption = $(this).children('img:eq(1)').attr('alt');
				/**
				 * Use math for good (not for evil) to calculate the width of the
				 * mask (the after image) by adjusting it with the left and right
				 * gap options.
				 */
				if ((diff > settings.left_gap) &&
					(diff < width - settings.right_gap)) {
						$(this).children('.ba-mask').width(diff);
				}
				/**
				 * Show a different caption when we move the mouse across the
				 * container.
				 */
				if (diff < (width * settings.reveal)) {
					$(this).children('.ba-caption').html(before_caption);
				} else {
					$(this).children('.ba-caption').html(after_caption);
				}
			});
		}
	});
})(jQuery);

CSS (before-after.css)

The CSS for this is pretty basic, we have three visible elements, the mask (the after image), the background (the before image) and the caption text.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.ba-mask {
	position: absolute;
	top: 0;
	left: 0;
	z-index: 100;
	border-right: 3px solid #333;
	overflow: hidden;
	box-shadow: 5px 5px 10px 0 rgba(0, 0, 0, 0.6);
	cursor: ew-resize;
}

.ba-bg {
	position: absolute;
	top: 0;
	left: 0;
	z-index: 0;
}

.ba-caption {
	position: absolute;
	bottom: 10px;
	left: 10px;
	z-index: 120;
	background: rgba(0, 0, 0, 0.8);
	color: #fff;
	padding: 2px 10px;
	display: none;
}

All that is left it to trigger the before_after() plugin function on the container (and pass some options if you want to change the defaults).

1
2
3
4
$('.gallery').before_after({
	left_gap: 0,
	caption: true
});

Options

 Default valueDescription
captionfalseToggle the image captions.
left_gap10The gap to the left of the image, in px.
right_gap10The gap to the right of the image, in px.
gap50The default gap shown before any interactions, in px.
reveal0.5Show the caption if you scroll below this, in px.

Download

You can view (and download) the files separately, JS file , CSS file or download a zip archive including everything.

License

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.