Expanding Search Bar Deconstructed

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

ExpandingSearchBar

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.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

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!

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animationsโ€”zero fluff, all quality. Make your Mondays and Thursdays creative!

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!

  17. looks good, you can add some effect as it to a modernize looks. thanks for nice tutorial.

  18. The JavaScript part would have been more simple with jQuery I guess… ๐Ÿ™‚
    Thanks for the tutorial anyway! ๐Ÿ™‚

  19. I agree with imac. Too much code for simple functionality. jQuery would be better here if there is no 100% rule to only use pure JS.

  20. Nice article… Thank’s very much, but i need to modified the search button with radius border, can u help me with the css? the search button still have a white space on the left corner :(, thank’s before..!!

    • I was wondering the same thing. I spent all day yesterday looking for a WordPress plug that does this exact thing… with no luck. But I am not sure where to even begin to put all this code into WordPress!

  21. Hey guys, I added this to my free web template but it won’t expand or close when clicked on. Any insight? I pretty much just included the files in the template, so there isn’t any problem with missing files.

  22. Hi, how can i use this in my index.php page?
    I tried to insert all links for css and js, but i can’t see the effect and the correct design of the search field in the page

    Thank you

  23. Thank you for this script, It has made my navigation bar look really clean and efficient ๐Ÿ™‚

  24. Hi! I tried to see the demo on my android and when i click in the button to start to write, all the page moves to the top and i can’t see what i’m writing … What is the solution?

  25. Great tutorial. How could I have the input field slide from the right side instead of the left?

    Thanks!

    • Hi Justin,
      you can flip it by doing the following:
      .sb-search { float:left; }
      .sb-search-input { left:0; padding: 20px 20px 20px 65px;} //makes sure the search button doesn’t cover your text
      .sb-icon-search,
      .sb-search-submit {
      left:0;
      }

      make sure you remove any references to right:0; in the positioning.
      That should cover it

    • Did you notice how the search icon jumps a little to the left, when the search box starts opening? I guess it’s because of the focus on the text input. One workaround is to disable transition. Is there any real solution?

  26. Nice work: how do you enable the searchbox to expand automatically when page is opened as opposed to clicking?

  27. This looks exactly what i’m after because I’m reducing the use of sidebars in WordPress so need something to work outside of that area. Just one question how easy would it be to switch this to a fixed position so it always stayed say half way up the browser window irrespective of scroll? I’m going to have a play with this at some point for sure.

  28. Hi there,
    Great Tutorial and great feature for an modern website.
    However i have an question. My search button is last on right, can i make (and how) other 3 items left of search hidden when box is expanded?

    Many thanks.

  29. Hello! I have some doubts… I downloaded the RAR archive and read the tutorial but I donยดt understand yet.

    The RAR archive have two css but I donยดt know if I need change anything or only cut and copy, or maybe choose between one or other. I have not very clear in what order and paste the codes. Can you help me?

    Thanks, I really love your tutorials

  30. Thank you, this is great. I have a question: I’ve implemented this on a site I’m developing, and upon expanding, the search box is pushing the content to its left (which is a nav menu unordered list, similar to the social icons list in the header of this page) and forcing it down below the expanded search box, as opposed to simply covering/overlaying it. Any idea what might be going on?

  31. This “tutorial” is obviously aimed at people already familiar with coding websites. Can anybody help me fudge this into WordPress? ๐Ÿ˜€