Accessible Responsive Menu in Genesis

Posted on
5/5 - (483 votes)

Các chủ đề con Genesis mới hơn như Workstation Pro và Atmosphere Pro đi kèm với mã menu hamburger di động đáp ứng tốt hơn mã trong các chủ đề cũ hơn như Beautiful Pro hoặc Magazine Pro ở chỗ khả năng truy cập được quan tâm.

Trong bài viết này, tôi chia sẻ mã được lấy từ Workstation Pro và được sửa đổi một chút để nó hoạt động không chỉ cho Menu chính mà còn cả Menu Header và Menu phụ.

Bước 1

Tạo tệp có tên  responsive-menu.js  trong thư mục chủ đề con của bạn  js (tạo nếu không có) có mã sau:

( function ( document, $, undefined ) {

	$( 'body' ).addClass( 'js' );

	'use strict';

	var responsive_menu     = {},
		mainMenuButtonClass = 'menu-toggle',
		subMenuButtonClass  = 'sub-menu-toggle';

	responsive_menu.init = function() {
		var toggleButtons = {
			menu : $( '', {
				'class' : mainMenuButtonClass,
				'aria-expanded' : false,
				'aria-pressed' : false,
				'role' : 'button'
				} )
				.append( responsive_menu.params.mainMenu ),
			submenu : $( '', {
				'class' : subMenuButtonClass,
				'aria-expanded' : false,
				'aria-pressed' : false,
				'role' : 'button'
				} )
				.append( $( '', {
					'class' : 'screen-reader-text',
					text : responsive_menu.params.subMenu
				} ) )
		};
		$( '.nav-primary, .nav-secondary, .nav-header' ).before( toggleButtons.menu ); // add the main nav buttons
		$( 'nav .sub-menu' ).before( toggleButtons.submenu ); // add the submenu nav buttons
		$( '.' + mainMenuButtonClass ).each( _addClassID );
		$( window ).on( 'resize.responsive_menu', _doResize ).triggerHandler( 'resize.responsive_menu' );
		$( '.' + mainMenuButtonClass ).on( 'click.responsive_menu-mainbutton', _mainmenuToggle );
		$( '.' + subMenuButtonClass ).on( 'click.responsive_menu-subbutton', _submenuToggle );
	};

	// add nav class and ID to related button
	function _addClassID() {
		var $this = $( this ),
			nav   = $this.next( 'nav' ),
			id    = 'class';
		$this.addClass( $( nav ).attr( 'class' ) );
		if ( $( nav ).attr( 'id' ) ) {
			id = 'id';
		}
		$this.attr( 'id', 'mobile-' + $( nav ).attr( id ) );
	}

	// check CSS rule to determine width
	function _combineMenus(){
		if ($( '.js nav' ).css( 'position' ) == 'relative' ){ // depends on .js nav having position: relative; in style.css
			$( 'ul.menu-secondary > li' ).addClass( 'moved-item' ); // tag moved items so we can move them back
			$( 'ul.menu-secondary > li' ).appendTo( 'ul.menu-primary' );
			$( '.nav-secondary' ).hide();
		} else {
			$( '.nav-secondary' ).show();
			$( 'ul.menu-primary > li.moved-item' ).appendTo( 'ul.menu-secondary' );
			$( 'ul.menu-secondary > li' ).removeClass( 'moved-item' );
		}
	}

	// Change Skiplinks and Superfish
	function _doResize() {
		var buttons = $( 'button[id^=mobile-]' ).attr( 'id' );
		if ( typeof buttons === 'undefined' ) {
			return;
		}
		_superfishToggle( buttons );
		_changeSkipLink( buttons );
		_maybeClose( buttons );
	}

	/**
	 * action to happen when the main menu button is clicked
	 */
	function _mainmenuToggle() {
		var $this = $( this );
		_toggleAria( $this, 'aria-pressed' );
		_toggleAria( $this, 'aria-expanded' );
		$this.toggleClass( 'activated' );
		// $( 'nav.nav-primary' ).slideToggle( 'fast' ); //changed to .nav-primary since we're not toggling .nav-secondary
		$this.next('nav').slideToggle( 'fast' );

	}

	/**
	 * action for submenu toggles
	 */
	function _submenuToggle() {

		var $this  = $( this ),
			others = $this.closest( '.menu-item' ).siblings();
		_toggleAria( $this, 'aria-pressed' );
		_toggleAria( $this, 'aria-expanded' );
		$this.toggleClass( 'activated' );
		$this.next( '.sub-menu' ).slideToggle( 'fast' );

		others.find( '.' + subMenuButtonClass ).removeClass( 'activated' ).attr( 'aria-pressed', 'false' );
		others.find( '.sub-menu' ).slideUp( 'fast' );

	}

	/**
	 * activate/deactivate superfish
	 */
	function _superfishToggle( buttons ) {
		if ( typeof $( '.js-superfish' ).superfish !== 'function' ) {
			return;
		}
		if ( 'none' === _getDisplayValue( buttons ) ) {
			$( '.js-superfish' ).superfish( {
				'delay': 100,
				'animation': {'opacity': 'show', 'height': 'show'},
				'dropShadows': false
			});
		} else {
			$( '.js-superfish' ).superfish( 'destroy' );
		}
	}

	/**
	 * modify skip links to match mobile buttons
	 */
	function _changeSkipLink( buttons ) {
		var startLink = 'genesis-nav',
			endLink   = 'mobile-genesis-nav';
		if ( 'none' === _getDisplayValue( buttons ) ) {
			startLink = 'mobile-genesis-nav';
			endLink   = 'genesis-nav';
		}
		$( '.genesis-skip-link a[href^="#' + startLink + '"]' ).each( function() {
			var link = $( this ).attr( 'href' );
			link = link.replace( startLink, endLink );
			$( this ).attr( 'href', link );
		});
	}

	function _maybeClose( buttons ) {
		if ( 'none' !== _getDisplayValue( buttons ) ) {
			return;
		}
		$( '.menu-toggle, .sub-menu-toggle' )
			.removeClass( 'activated' )
			.attr( 'aria-expanded', false )
			.attr( 'aria-pressed', false );
		$( 'nav, .sub-menu' )
			.attr( 'style', '' );
	}

	/**
	 * generic function to get the display value of an element
	 * @param  {id} $id ID to check
	 * @return {string}     CSS value of display property
	 */
	function _getDisplayValue( $id ) {
		var element = document.getElementById( $id ),
			style   = window.getComputedStyle( element );
		return style.getPropertyValue( 'display' );
	}

	/**
	 * Toggle aria attributes
	 * @param  {button} $this     passed through
	 * @param  {aria-xx} attribute aria attribute to toggle
	 * @return {bool}           from _ariaReturn
	 */
	function _toggleAria( $this, attribute ) {
		$this.attr( attribute, function( index, value ) {
			return 'false' === value;
		});
	}

	$(document).ready(function () {

		// run test on initial page load
		// _combineMenus();

		// run test on resize of the window
		// $( window ).resize( _combineMenus );

		responsive_menu.params = typeof ResponsiveMenuL10n === 'undefined' ? '' : ResponsiveMenuL10n;

		if ( typeof responsive_menu.params !== 'undefined' ) {
			responsive_menu.init();
		}

	});

})( document, jQuery );

Bước 2

Thêm phần sau vào functions.php của chủ đề con  :

// Enqueue Scripts and Styles
add_action( 'wp_enqueue_scripts', 'custom_enqueue_scripts_styles' );
function custom_enqueue_scripts_styles() {

	// wp_enqueue_style( 'google-fonts', '//fonts.googleapis.com/css?family=Roboto+Condensed:300italic,700italic,700,300', array(), CHILD_THEME_VERSION );
	wp_enqueue_style( 'dashicons' );

	wp_enqueue_script( 'responsive-menu', get_stylesheet_directory_uri() . '/js/responsive-menu.js', array( 'jquery' ), '1.0.0', true );
	$output = array(
		'mainMenu' => __( 'Menu', 'my-theme-text-domain' ),
		'subMenu'  => __( 'Menu', 'my-theme-text-domain' ),
	);
	wp_localize_script( 'responsive-menu', 'ResponsiveMenuL10n', $output );

}

Bước 3

Thêm phần sau vào style.css của chủ đề con  :

/* Responsive Menu
--------------------------------------------- */

.sub-menu-toggle,
.menu-toggle {
	display: none;
	visibility: hidden;
}

@media only screen and (max-width: 880px) {

	.js nav {
		display: none;
		position: relative;
	}

	.js nav .wrap {
		padding: 0;
	}

	.js nav.pagination {
		display: block;
	}

	.menu-toggle,
	.sub-menu-toggle {
		background-color: #222;
		color: #fff;
		display: block;
		margin: 0 auto;
		overflow: hidden;
		text-align: center;
		visibility: visible;
	}

	.menu-toggle {
		position: relative;
		right: 0;
		z-index: 1000;
		width: 100%;
	}

	.menu-toggle:before,
	.menu-toggle.activated:before {
		color: #fff;
		content: "\f333";
		display: inline-block;
		font: normal 20px/1 'dashicons';
		margin: 0 auto;
		padding-right: 10px;
		text-rendering: auto;
		-webkit-transform: translate(0, 0);
		-ms-transform:		 translate(0, 0);
		transform:				 translate(0, 0);
		/*vertical-align: bottom;*/
		vertical-align: top;
		padding-top: 3px;
	}

	.sub-menu-toggle {
		float: right;
		padding: 18px;
		position: absolute;
		right: 0;
		top: 0;
		z-index: 100;
	}

	.sub-menu-toggle:before {
		content: "\f347";
		display: inline-block;
		font: normal 16px/1 'dashicons';
		text-rendering: auto;
		-webkit-transform: translate(0, 0);
		-ms-transform:		 translate(0, 0);
		transform:				 translate(0, 0);
	}

	.sub-menu-toggle.activated:before {
		content: "\f343";
	}

	.js .genesis-nav-menu .menu-item {
		display: block;
		float: none;
		position: relative;
		text-align: left;
	}

	.js .genesis-nav-menu .menu-item:hover {
		position: relative;
	}

	.js .genesis-nav-menu .menu-item a {
		border: none;
		color: #fff;
		/*font-family: 'Roboto Condensed', sans-serif;*/
		padding: 20px;
		width: 100%;
	}

	.js .genesis-nav-menu .menu-item a:hover,
	.js .genesis-nav-menu .sub-menu {
		border: none;
	}

	.js .genesis-nav-menu .menu-item > a:focus ul.sub-menu,
	.js .genesis-nav-menu .menu-item > a:focus ul.sub-menu .sub-menu {
		left: 0;
		margin-left: 0;
	}

	.js .genesis-nav-menu > .menu-item-has-children > a:after {
		content: none;
	}

	.js .genesis-nav-menu .sub-menu {
		clear: both;
		display: none;
		margin: 0;
		opacity: 1;
		position: static;
		width: 100%;
		padding-left: 20px;
	}

	.js .genesis-nav-menu .sub-menu .sub-menu {
		margin: 0;
	}

	.js .genesis-nav-menu .sub-menu a {
		border: none;
	}

	.js nav .genesis-nav-menu .menu-item .sub-menu li a,
	.js nav .genesis-nav-menu .menu-item .sub-menu li a:hover,
	.js nav button:hover,
	.js .menu-toggle:hover,
	.js .nav-primary {
		background-color: #222;
		color: #fff;
	}

	.js nav .genesis-nav-menu .menu-item .sub-menu li a:focus,
	.js nav .genesis-nav-menu .menu-item a:focus,
	.js nav button:focus,
	.js .menu-toggle:focus {
		background-color: #222;
		color: #ff4800;
	}

	.js .nav-header .genesis-nav-menu .menu-item a,
	.js .nav-secondary .genesis-nav-menu .menu-item a {
		color: #333;
	}

}