Expanding Search Bar Deconstructed

A tutorial on how to create a mobile-friendly and responsive expanding search bar.
ExpandingSearchBar

From our monthly sponsor: Create your beautiful portfolio website with Squarespace. Start your free trial.Advertisement

Maybe you have noticed the little adjustments that we did to our theme lately here on Codrops. One of the things we thought might be nice to change is the search input. We’ve put it from the sidebar into the header and we use a common effect where you have to click to expand the input. We have received many requests on explaining how we did it and today we’d like to show you how to create a search input like that from scratch. The aim is to maximize compatibility for mobile devices and older browsers (down to IE8). Although this might seem like a super-easy thing, we’ll have to apply some tricks in order to make it work properly.

Summarized, this is what we want the search component to do:

  • Initially, we only want to show a button with a search icon.
  • When clicking on the icon, we want a search input to slide out.
  • The component should be fluid, meaning that we can use it in a responsive context.
  • When we type something we want to be able to submit the form by either hitting enter or clicking on the search icon.
  • If the input is expanded and empty, and we click on the search icon, we want the input to close again.
  • We also want the input to close when we click outside of the search bar, be it empty or not.
  • Without JavaScript, we want the search bar to be shown expanded.
  • For a smoother experience on touch devices, we’ll also want to add support for touch events.

Now that we know what we want to do, let’s start with the markup.

The Markup

For the markup we need a main container, a form, the text and submit inputs, and a span for the icon:

<div id="sb-search" class="sb-search">
	<form>
		<input class="sb-search-input" placeholder="Enter your search term..." type="search" value="" name="search" id="search">
		<input class="sb-search-submit" type="submit" value="">
		<span class="sb-icon-search"></span>
	</form>
</div>

Usually, we could use a pseudo-element for the icon, but because it’s not intended to be used on replaced elements like form elements, we will simply use a span in this case.

With all the elements in place, let’s start styling them.

The CSS

Following our requirements, we should first make sure that we have a button with a search icon visible. The rest should be hidden. But let’s also think one step ahead and imagine what happens when we enlarge the search bar (the main wrapper). How do we do this? Let’s make use of overflow: hidden and expanding the width of the sb-search wrapper should reveal the input.

So, the first thing we style is the sb-search wrapper. We make it float on the right side and set the overflow to hidden. The width should naturally be 60px but since we will want to animate to a width of 100%, be will get in trouble on mobile (iOS) browsers. They don’t like to transition from a pixel-based width to a percentage-based one. They’ll simply omit the transition. So instead we define a min-width value of 60px and a width of 0%. This brilliant solution is by @julienknebel and he writes about it here: CSS transition from a fixed px width to an auto width.

We’ll also add a transition for the width and -webkit-backface-visibility: hidden to avoid some traces of the input for mobile (iOS) browsers:

.sb-search {
	position: relative;
	margin-top: 10px;
	width: 0%;
	min-width: 60px;
	height: 60px;
	float: right;
	overflow: hidden;

	-webkit-transition: width 0.3s;
	-moz-transition: width 0.3s;
	transition: width 0.3s;

	-webkit-backface-visibility: hidden;
}

Everything that is overflowing this little box, won’t be visible.

Now, let’s position the search input. We’ll set a percentage-based width so that when we expand the parent, the input will expand with it. Setting the right height, font-size and padding will ensure that the text is centered (using line-height won’t work as expected in IE8, so let’s set the paddings instead).
Setting the input to position absolute might not seem necessary, but it solves a nasty little thing that happens sometimes when closing the search: the input seems visible on the right side for a very short moment.

.sb-search-input {
	position: absolute;
	top: 0;
	right: 0;
	border: none;
	outline: none;
	background: #fff;
	width: 100%;
	height: 60px;
	margin: 0;
	z-index: 10;
	padding: 20px 65px 20px 20px;
	font-family: inherit;
	font-size: 20px;
	color: #2c3e50;
}

input[type="search"].sb-search-input {
	-webkit-appearance: none;
	-webkit-border-radius: 0px;
}

Additionally, we are removing the default styles of the search input for WebKit browsers.

Let’s define the color of the placeholder text with these vendor-specific rules:

.sb-search-input::-webkit-input-placeholder {
	color: #efb480;
}

.sb-search-input:-moz-placeholder {
	color: #efb480;
}

.sb-search-input::-moz-placeholder {
	color: #efb480;
}

.sb-search-input:-ms-input-placeholder {
	color: #efb480;
}

Now, let’s take care of the search icon button and the submit input. We know that we want them in the same place, so let’s position them on the right corner and set the same dimensions. Since they will be on top of each other, we’ll set them to absolute positioning:

.sb-icon-search,
.sb-search-submit  {
	width: 60px;
	height: 60px;
	display: block;
	position: absolute;
	right: 0;
	top: 0;
	padding: 0;
	margin: 0;
	line-height: 60px;
	text-align: center;
	cursor: pointer;
}

Initially, we will want to have the icon button clickable. Then, when we open the input, we want the submit input to be clickable. So, well set the z-index of the submit input to -1 in the beginning and make it transparent, so that we always see the search icon span:

.sb-search-submit {
	background: #fff; /* IE needs this */
	-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; /* IE 8 */
    filter: alpha(opacity=0); /* IE 5-7 */
    opacity: 0;
	color: transparent;
	border: none;
	outline: none;
	z-index: -1;
}

Why not just setting the background to transparent? That does not seem to work nicely in IE because the element is not clickable like that. So we use a solid background color and set the opacity to 0 instead.

The search icon span will have a high z-index initially, because we want it to be on top of everything. We’ll use the pseudo-element :before to add the search icon:

.sb-icon-search {
	color: #fff;
	background: #e67e22;
	z-index: 90;
	font-size: 22px;
	font-family: 'icomoon';
	speak: none;
	font-style: normal;
	font-weight: normal;
	font-variant: normal;
	text-transform: none;
	-webkit-font-smoothing: antialiased;
}

.sb-icon-search:before {
	content: "e000";
}

Let’s not forget to include the web font at the beginning of our CSS:

/* Search icon by IcoMoon, made with http://icomoon.io/app/ */
@font-face {
	font-family: 'icomoon';
	src:url('../fonts/icomoon/icomoon.eot');
	src:url('../fonts/icomoon/icomoon.eot?#iefix') format('embedded-opentype'),
		url('../fonts/icomoon/icomoon.woff') format('woff'),
		url('../fonts/icomoon/icomoon.ttf') format('truetype'),
		url('../fonts/icomoon/icomoon.svg#icomoon') format('svg');
	font-weight: normal;
	font-style: normal;
}

With the styles defined the way we did, we can now simply set the width of the sb-search wrapper to be 100% when we add the sb-search-open class. Without JavaScript, we want the search input to be open by default:

.sb-search.sb-search-open,
.no-js .sb-search {
	width: 100%;
}

Let’s change the color of the search icon span and put it below the submit input by setting the z-index to a lower value:

.sb-search.sb-search-open .sb-icon-search,
.no-js .sb-search .sb-icon-search {
	background: #da6d0d;
	color: #fff;
	z-index: 11;
}

And finally, we set the submit input’s z-index to a higher value so that we can click on it:

.sb-search.sb-search-open .sb-search-submit,
.no-js .sb-search .sb-search-submit {
	z-index: 90;
}

With all the necessary styles defined, let’s do the JavaScript.

The JavaScript

Let’s start by toggling the sb-search-open class. We add the class when we click on the main wrapper (sb-search) and we remove it when we click on the submit input, only if the search input is empty. Otherwise we submit the form. In order not to trigger the class removal when we click on the input (since our trigger is the whole wrapper), we need to prevent the click event bubbling on that element. This means that clicking on the input will not cause a click being triggered on its parents.

;( function( window ) {
	
	function UISearch( el, options ) {	
		this.el = el;
		this.inputEl = el.querySelector( 'form > input.sb-search-input' );
		this._initEvents();
	}

	UISearch.prototype = {
		_initEvents : function() {
			var self = this,
				initSearchFn = function( ev ) {
					if( !classie.has( self.el, 'sb-search-open' ) ) { // open it
						ev.preventDefault();
						self.open();
					}
					else if( classie.has( self.el, 'sb-search-open' ) && /^s*$/.test( self.inputEl.value ) ) { // close it
						self.close();
					}
				}

			this.el.addEventListener( 'click', initSearchFn );
			this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); });
		},
		open : function() {
			classie.add( this.el, 'sb-search-open' );
		},
		close : function() {
			classie.remove( this.el, 'sb-search-open' );
		}
	}

	// add to global namespace
	window.UISearch = UISearch;

} )( window );

Next, we’ll need to add the events for removing the sb-search-open class when we click somewhere else, outside of our search bar. For this to work we also need to take care of the event bubbling when clicking on the main wrapper.

;( function( window ) {
	
	function UISearch( el, options ) {	
		this.el = el;
		this.inputEl = el.querySelector( 'form > input.sb-search-input' );
		this._initEvents();
	}

	UISearch.prototype = {
		_initEvents : function() {
			var self = this,
				initSearchFn = function( ev ) {
					ev.stopPropagation();
					
					if( !classie.has( self.el, 'sb-search-open' ) ) { // open it
						ev.preventDefault();
						self.open();
					}
					else if( classie.has( self.el, 'sb-search-open' ) && /^s*$/.test( self.inputEl.value ) ) { // close it
						self.close();
					}
				}

			this.el.addEventListener( 'click', initSearchFn );
			this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); });
		},
		open : function() {
			var self = this;
			classie.add( this.el, 'sb-search-open' );
			// close the search input if body is clicked
			var bodyFn = function( ev ) {
				self.close();
				this.removeEventListener( 'click', bodyFn );
			};
			document.addEventListener( 'click', bodyFn );
		},
		close : function() {
			classie.remove( this.el, 'sb-search-open' );
		}
	}

	// add to global namespace
	window.UISearch = UISearch;

} )( window );

Another thing that we want to take care of is trimming the search term.
Also, when we click on the search icon, we want the input to be focused. Since this causes some jerky transition on mobile (iOS) browsers (the keyboard will open at the same time), we will want to avoid that for this case. When we close the search bar, we’ll blur the search input. This will solve some issues on some devices that show the cursor blinking even after the input is closed.

;( function( window ) {

	// http://stackoverflow.com/a/11381730/989439
	function mobilecheck() {
		var check = false;
		(function(a){if(/(android|ipad|playbook|silk|bbd+|meego).+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
		return check;
	}

	// http://www.jonathantneal.com/blog/polyfills-and-prototypes/
	!String.prototype.trim && (String.prototype.trim = function() {
		return this.replace(/^s+|s+$/g, '');
	});
	
	function UISearch( el, options ) {	
		this.el = el;
		this.inputEl = el.querySelector( 'form > input.sb-search-input' );
		this._initEvents();
	}

	UISearch.prototype = {
		_initEvents : function() {
			var self = this,
				initSearchFn = function( ev ) {
					ev.stopPropagation();
					// trim its value
					self.inputEl.value = self.inputEl.value.trim();
					
					if( !classie.has( self.el, 'sb-search-open' ) ) { // open it
						ev.preventDefault();
						self.open();
					}
					else if( classie.has( self.el, 'sb-search-open' ) && /^s*$/.test( self.inputEl.value ) ) { // close it
						self.close();
					}
				}

			this.el.addEventListener( 'click', initSearchFn );
			this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); });
		},
		open : function() {
			var self = this;
			classie.add( this.el, 'sb-search-open' );
			// focus the input
			if( !mobilecheck() ) {
				this.inputEl.focus();
			}
			// close the search input if body is clicked
			var bodyFn = function( ev ) {
				self.close();
				this.removeEventListener( 'click', bodyFn );
			};
			document.addEventListener( 'click', bodyFn );
		},
		close : function() {
			this.inputEl.blur();
			classie.remove( this.el, 'sb-search-open' );
		}
	}

	// add to global namespace
	window.UISearch = UISearch;

} )( window );

For everything to work smoothly on mobile devices, we’ll need to add the respective touch events. Adding preventDefault in the initSearchFn function will prevent the touch and the click events to trigger simultaneously on touch devices.

;( function( window ) {
	
	// http://stackoverflow.com/a/11381730/989439
	function mobilecheck() {
		var check = false;
		(function(a){if(/(android|ipad|playbook|silk|bbd+|meego).+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
		return check;
	}
	
	// http://www.jonathantneal.com/blog/polyfills-and-prototypes/
	!String.prototype.trim && (String.prototype.trim = function() {
		return this.replace(/^s+|s+$/g, '');
	});

	function UISearch( el, options ) {	
		this.el = el;
		this.inputEl = el.querySelector( 'form > input.sb-search-input' );
		this._initEvents();
	}

	UISearch.prototype = {
		_initEvents : function() {
			var self = this,
				initSearchFn = function( ev ) {
					ev.stopPropagation();
					// trim its value
					self.inputEl.value = self.inputEl.value.trim();
					
					if( !classie.has( self.el, 'sb-search-open' ) ) { // open it
						ev.preventDefault();
						self.open();
					}
					else if( classie.has( self.el, 'sb-search-open' ) && /^s*$/.test( self.inputEl.value ) ) { // close it
						ev.preventDefault();
						self.close();
					}
				}

			this.el.addEventListener( 'click', initSearchFn );
			this.el.addEventListener( 'touchstart', initSearchFn );
			this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); });
			this.inputEl.addEventListener( 'touchstart', function( ev ) { ev.stopPropagation(); } );
		},
		open : function() {
			var self = this;
			classie.add( this.el, 'sb-search-open' );
			// focus the input
			if( !mobilecheck() ) {
				this.inputEl.focus();
			}
			// close the search input if body is clicked
			var bodyFn = function( ev ) {
				self.close();
				this.removeEventListener( 'click', bodyFn );
				this.removeEventListener( 'touchstart', bodyFn );
			};
			document.addEventListener( 'click', bodyFn );
			document.addEventListener( 'touchstart', bodyFn );
		},
		close : function() {
			this.inputEl.blur();
			classie.remove( this.el, 'sb-search-open' );
		}
	}

	// add to global namespace
	window.UISearch = UISearch;

} )( window );

Finally, for browsers that don’t support the addEventListener and removeEventListener we use Jonathan Neal’s EventListener polyfill.

// EventListener | @jon_neal | //github.com/jonathantneal/EventListener
!window.addEventListener && window.Element && (function () {
	function addToPrototype(name, method) {
		Window.prototype[name] = HTMLDocument.prototype[name] = Element.prototype[name] = method;
	}

	var registry = [];

	addToPrototype("addEventListener", function (type, listener) {
		var target = this;

		registry.unshift({
			__listener: function (event) {
				event.currentTarget = target;
				event.pageX = event.clientX + document.documentElement.scrollLeft;
				event.pageY = event.clientY + document.documentElement.scrollTop;
				event.preventDefault = function () { event.returnValue = false };
				event.relatedTarget = event.fromElement || null;
				event.stopPropagation = function () { event.cancelBubble = true };
				event.relatedTarget = event.fromElement || null;
				event.target = event.srcElement || target;
				event.timeStamp = +new Date;

				listener.call(target, event);
			},
			listener: listener,
			target: target,
			type: type
		});

		this.attachEvent("on" + type, registry[0].__listener);
	});

	addToPrototype("removeEventListener", function (type, listener) {
		for (var index = 0, length = registry.length; index < length; ++index) {
			if (registry[index].target == this && registry[index].type == type && registry[index].listener == listener) {
				return this.detachEvent("on" + type, registry.splice(index, 1)[0].__listener);
			}
		}
	});

	addToPrototype("dispatchEvent", function (eventObject) {
		try {
			return this.fireEvent("on" + eventObject.type, eventObject);
		} catch (error) {
			for (var index = 0, length = registry.length; index < length; ++index) {
				if (registry[index].target == this && registry[index].type == eventObject.type) {
					registry[index].call(this, eventObject);
				}
			}
		}
	});
})();

And that’s it! We hope you enjoyed this tutorial and find it useful!

Tagged with:

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

http://www.codrops.com

Receive our bi-weekly Collective or official newsletter right in your inbox.

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.

Feedback 98

Comments are closed.
  1. A Special thanks codrop guys, i been looking this since long time back..
    I did this on CSS width transaction but its looks so good, But this is the thing i wanted.

  2. Wow Manoela, great it’s supported on mobile/touch screen too.

    One thing… If you use the HTML5 placeholder, than why not input type=”search” ?

    • Hey Ralph, yes that would of course be the type to use here, I just wanted the same styling for every browser so I went for the usual text input 🙂 Thanks for your feedback, cheers, ML

    • Yeah, indeed…. search inputs are a pain in the *beep* to style the same in each and every browser/device if not impossible.

    • Hey, I guess it’s solved now 🙂 Sean’s tip helped me remove the default styling!

  3. Thanks hugely for this tutorial and all the code Mary Lou. Looks and works well. Especially like that you’ve included the touch events for devices that use them. Should be easy to go from what you have created and switch to pointer events once they become an adopted standard & are implemented. Great job!

  4. I can’t help but feel this is code for the sake of writing code? I can’t believe it takes so many lines to write what this has really achieved.

    • Honestly, I felt the same while reading it. I also wonder if there is much of an impact on loading times. Seems a lot for a little.

  5. Nice job Mary you are a champion! Funny how much code overhead is required to overcome so many obstacles for such a small usability tweak.

    • Thanks Dave! Yes, the more one tries to make things cross-device, cross-browser (and browser version) compatible, the more complicated things get. But I’m very happy with the behavior and the result. Thanks for your feedback, ML

  6. Hi, great tutorial along with everything else on this site! I’ve actually been looking for this a long time. Would the slideout also be possible with, for example, a country/language selection menu?

  7. For the markup, input type should be type="search" and not text. You can get the same appearance as textfields with -webkit-appearance: textfield;. Read more here.

    • Thanks for the tip Sean! Input type=”search” it is! But you’ll need to use -webkit-appearance: none and -webkit-border-radius: 0px if you want to get rid of the rounded borders on iOS.

  8. Great timing! I was just thinking about adding this functionality in a layout I’m only just starting to design. Thanks!

  9. Just in love with ML tutorials, very helpful for me and ofcourse for others. Good job!!

  10. On FF sometimes search in codrops layout disappeares immediatly after showing (without entering blur – I mean I was still able to type text into it)

  11. cool, any idea why do you use classie.js? am I the only one missing this part of js code

  12. Hi okay?
    What did you do to change the function of the url? For padão is: “?search=”
    need to put: “?=s”
    It’s why I use wodpress, but already put inside the form: action=”” method=”get”

    What should I do?

  13. Fantastic! Great tutorial. I’ve been procrastinating on this for too long now. Thanks for the share.

    Maybe you have noticed the little adjustments that we did to our theme lately here on Codrops.

    Yeah I noticed the design has been become how can I say it… more “flat“. You guys have really executed well on this one. Keep up the great work guys.

  14. As usual, your articles give me inspiration to continue my journey as an aspiring web developer. You are the best!

  15. Nicely done but I also feel that there’s too much code to complete this. Must be an easier way to code this.

  16. Very cool. I’m going to try and build it using only CSS, for a(nother) little weekend project. Thanks for writing this up!