As our number of mobile web visitors continues to increase, we want to make sure that the user experience is great on small screens. This quarter, we took some time to assess our reusable UI components and look for ways to improve their behavior across devices.

We found a lot of room for improvement in our dropdown component - it looked great on desktop, but it was hard (i.e., in some cases it’d go offscreen or touch/tap being not very smooth) to use on mobile devices.

Story

We built our dropdown component to be generic enough to handle any use case our developers might need. Some dropdowns contains simple redirects, some are hooked up to make AJAX requests or sort elements on the page. We wanted to keep our changes to the dropdown component backwards compatible with all these behaviors.

Our first thought was if we could improve it by just adding some media queries or JavaScript but that would still make cross-platform testing difficult. We also thought to replace dropdown with native select component (using the HTML <select/> tag). But, that would force us to deal with problems inherent to native select elements. We would need to write JS fallbacks and include other graceful degradations in order to style our native select elements to look like custom dropdowns.

Solution

We decided to show native selects on touch devices instead of a custom dropdown menu – a hybrid solution of our options. Our custom dropdown template and CSS files were left untouched; all the magic happens in the JavaScript component. When someone clicks or taps on the dropdown toggle, we detect whether the screen supports touch events. If it does, we create a native select instead of showing custom dropdown menu.

dropdownToggle.on('click', function (event) {
    if (shouldEnableNative()) {
        showNativeSelect();
    } else {
        toggleCustomDropdownMenu();
    }
});
function shouldEnableNative() {
// Checks if screen support touch events
    return (('ontouchstart' in window) ||
        window.DocumentTouch && document instanceof window.DocumentTouch
    ) && window.matchMedia(dropdownOptions.mediaQuery || ‘all’).matches;
}

If shouldEnableNative() returns true, then we execute the showNativeSelect() method which creates a native select in the DOM.

function showNativeSelect() {
	// Creates a new <select/>
	var select = getSelect();
	var links = this.children.menu.find('.dropdown_link');

	// Prepares a list of options and wraps in select
	var selectOptions = links.map(function () {
		return $('<option>').text(this.textContent);
	});
	select.setHTML(selectOptions.get());

	// Registers change event of select to trigger respective
	// custom dropdown option
	select.one('change', function () {
		$(links.get(select[0].selectedIndex)).trigger('click');
		select.hide();
	}.bind(this));

	// Appends it to the container
	select.appendTo(this.container).show();

	// Opens native select on click of existing custom dropdown’s toggle
	var e = document.createEvent('MouseEvents');
	e.initMouseEvent('mousedown', true, true, window);
	select[0].dispatchEvent(e);
};

getSelect() creates a native select and returns it back to the showNativeSelect().

function getSelect() {
	var styles = {
		'position': 'absolute',
		'bottom': '0',
		'width': '0',
		'appearance': 'none',
		'border': '0'
	};

// Creates a new select in DOM and make it invisible
	return $('<select>', { 'class': 'js-dropdown-mobile-select' })
		.css(styles)
		.hide();
};

This solution not only saved our time but allowed us to maximize mobile accessibility while retaining custom dropdown styles for desktop users. And now, Gifshots!

Dropdowns on non-touch devices stays custom.

And, on touch supported devices becomes native.

Join Yelp as Front-end Engineer

Interested in building reusable, responsive components? Join Yelp! If you care about good code, good design, and an outstanding user experience, we want to hear from you!

View Job

Back to blog