Filter generator

by Comandeer

HTML

<div id="filters" class="filters">
	<label for="filter-contrast" class="filters__label">Contrast</label>
	<input id="filter-contrast" class="filters__value" type="range" name="contrast" data-unit="%" min="0" max="200" value="100">
	<label for="filter-saturate" class="filters__label">Saturate</label>
	<input id="filter-saturate" class="filters__value" type="range" name="saturate" data-unit="%" min="0" max="100" value="100">
	<label for="filter-brightness" class="filters__label">Brightness</label>
	<input id="filter-brightness" class="filters__value" type="range" name="brightness" data-unit="%" min="0" max="100" value="50">
	<label for="filter-color" class="filters__label">Color</label>
	<input id="filter-color" class="filters__value" type="range" name="hue-rotate" data-unit="deg" min="0" max="360" value="0">
	<label for="filter-sepia" class="filters__label">Sepia</label>
	<input id="filter-sepia" data-unit="%" class="filters__value" type="range" name="sepia" min="0" max="360" value="0">
</div>

<pre id="filters-output" class="filters-output"><code class="language-css"></code></pre>

<img src="https://www.comandeer.pl/images/custom/comandeer.jpg" alt="Preview of applied filters" id="filters-preview" class="filters-preview">

CSS

.filters {
	overflow: hidden;
}
.filters__label, .filters__value {
	float: left;
}
.filters__label {
	clear: left;
}

.filters-output {
	user-select: all;
	max-width: 100%;
	overflow: auto;
	white-space: pre-wrap;
	background: pink;
}

JavaScript

// IIFE żeby nic nie wyciekło
// http://benalman.com/news/2010/11/immediately-invoked-function-expression/
( function() {
	// Pobieramy potrzebne elementy.
	var filtersContainer = document.getElementById( 'filters' ),
		output = document.getElementById( 'filters-output' ),
		preview = document.getElementById( 'filters-preview' );
	
	// Deklaruję funkcję, która będzie mi pobierać wartość wszystkich filtrów. Jako parametr bierze kontener filtrów.
	function generateCSSString( container ) {
		// Pobieram wszystkie filtry i przetwarzam tę kolekcję na tablicę przy pomocy Array.from.
		var filters = Array.from( container.querySelectorAll( '.filters__value' ) );
		
		// Stosuję reduce, żeby wyciągnąć wartości ze wszystkich pól i złączyć w jeden string.
		// Być może czytelniej byłoby rozbić to na map, a potem reduce.
		return filters.reduce( ( code, input ) => {
			// Stosuję template literals, żeby nie bawić się w bezsensowne +.
			// Nazwa filtru jest w [name] pola, wartość we własności value, a jednostkę wrzuciłem do [data-unit].
			code += `${ input.name }(${ input.value }${ input.dataset.unit }) `;
			
			return code;
		}, '' )
		// Z racji tego, że produkujemy string, przepuszczamy go na końcu przez trim, żeby wywalić nadmiarową spację.
		.trim();
	}
	
	function updateOutput( outputElement, cssString ) {
		outputElement.textContent = `filter: ${ cssString };`;
	}
	
	function updatePreview( previewElement, cssString ) {
		previewElement.style.filter = cssString;
	}
	
	// W sumie jedyne, co tu chcemy zrobić, to wygenerować nowy CSS i wywołać updateOutput i updatePreview z nim jako parametrem,
	function update() {
		var cssString = generateCSSString( filtersContainer );
		
		updateOutput( output, cssString );
		updatePreview( preview, cssString );
	}
	
	// Teraz zastosuję event delegation, żeby nie przypinać tak dużo listenerów.
	filtersContainer.addEventListener( 'change', update );
	
	// Przy wczytaniu wywołujemy update, żeby wygenerować podgląd i...