As my 2nd year students work towards their graduate , my lecture content has increasingly included ways they might share and illustrate their portfolio work. Employers aren’t just interested in results: they are increasingly focused on process, the narrative behind a creative work. One way of demonstrating workflow is to use a “before and after” image comparison, coupled with a slide control. In the example above, I’m demonstrating retouching work on an image.

Previous solutions for this kind of gallery usually require considerable programming, often using a framework. But with HTML5 and the range input, we can reduce the code to just three lines of vanilla JavaScript and some CSS.

The HTML used in this example is slightly unusual in that it lacks any content: all images will be presented using the CSS background-image property.

<div id="comparison">
	<figure>
		<div id="divisor"></div>
	</figure>
	<input type="range" min="0" max="100" value="50" id="slider" oninput="moveDivisor()">
</div>

The #comparison div surrounds and controls everything, while the figure inside displays the “before” image, and the #divisor contains the “after” version.

Because this solution is responsive, image size doesn’t matter: what matters is that both images are the same size, with their subjects matched in position and perspective.

The CSS

The CSS to set up the images is slightly more complex:

div#comparison { 
	width: 60vw;
	height: 60vw;
	max-width: 600px;
	max-height: 600px;
	overflow: hidden; 
}
div#comparison figure { 
	background-image: url(photoshop-face-before.jpg);
	background-size: cover;
	font-size: 0;
	width: 100%;
	height: 100%;
	margin: 0; 
}
div#comparison figure > img {
	position: relative;
	width: 100%;
}
div#comparison figure div { 
	background-image: url(photoshop-face-after.jpg);
	background-size: cover;
	position: absolute;
	width: 50%; 
	box-shadow: 0 5px 10px -2px rgba(0,0,0,0.3);
	overflow: hidden;
	bottom: 0; height: 100%; 
}

As these images are square, I’m sizing their containers using the same vw measurement for both height and width; images with wider aspect ratios would require different values. The container for the “after” image has a slight shadow on its leading edge to clarify the distinction between the background and foreground; the figure’s font-size and margin have been set to 0 to remove all space in the elements. The inner div is set to 50% of the width of its container to show half of the “after” image, matching the initial value of the range slider.

Next, we turn to customizing the range slider. This is made more complex by the fact that customizing the pseudo-elements created in the DOM for the sliders must be specified separately for each browser:

input[type=range]{
	-webkit-appearance:none;
	-moz-appearance:none;
	position: relative;
	top: -2rem; left: -2%;
	background-color: rgba(255,255,255,0.1);
	width: 102%; 
}
input[type=range]:focus { 
	outline: none;
}
input[type=range]:active {
	outline: none;
}
input[type=range]::-moz-range-track {
	-moz-appearance:none;
	height:15px;
	width: 98%;
	background-color: rgba(255,255,255,0.1); 
	position: relative;
	outline: none;    
}
input[type=range]::active {
	border: none;
	outline: none;
}
input[type=range]::-webkit-slider-thumb {
	-webkit-appearance:none;
	width: 20px;
	height: 15px;
	background: #fff;
	border-radius: 0;
}
input[type=range]::-moz-range-thumb {
	-moz-appearance: none;
	width: 20px;
	height: 15px;
	background: #fff;
	border-radius: 0;
}   
input[type=range]:focus::-webkit-slider-thumb {
	background: rgba(255,255,255,0.5);
}
input[type=range]:focus::-moz-range-thumb {
	background: rgba(255,255,255,0.5);
}

The JavaScript

Finally, the moveDivisor() JavaScript function called by our range input, placed at the end of our page:

divisor = document.getElementById("divisor");
slider = document.getElementById("slider");
function moveDivisor() {
	divisor.style.width = slider.value+"%";
}

If you want to ensure that the divisor is reset after page load (since Firefox will remember the last value the slider contained) add a call to the same function after the window loads:

window.onload = function() {
	moveDivisor();
};

That’s all there is to it. Of course, there’s much more we could add – and will, in future articles – but that should be enough to get anyone interested in such a technique started with their own version of this effect.