I'm working on a klondike solitaire game and I created a menu that is displayed on the top of the page. On laptops and computers I want to look like regular menus, considering many players are used to such menus, but on mobile devices, I wanted the menu items to be easy to click.
<div class="menu-bar" id="menuBar"></div>
Here is the css style for the regular menu:
body {
padding: 0;
margin: 0;
}
/* Basic menu styling for desktop */
.menu-bar {
display: flex;
background-color: #333;
padding: 10px;
justify-content: flex-start;
/* Align menu items to the left */
font-family: Arial, Helvetica, sans-serif
}
.menu-item {
color: white;
padding: 10px;
cursor: pointer;
position: relative;
text-align: center;
flex: 0;
display: flex;
/* Align icon and text on the same line */
align-items: center;
justify-content: center;
white-space: nowrap;
}
.menu-item {
text-align: left;
}
.menu-item i {
font-size: 1.5rem;
margin-right: 8px;
/* Space between icon and text */
}
.menu-item .shortcut,
.submenu-item .shortcut {
color: #999;
}
.menu-item .shortcut {
display: none;
/* too ugly on main items */
}
.submenu {
display: none;
position: absolute;
background-color: #444;
top: 40px;
left: 0;
width: 150px;
}
.submenu-item {
padding: 10px;
color: white;
}
.submenu-item:hover {
background-color: #666;
}
.menu-item.active .submenu {
display: block;
}
To make the menu act differently on mobile devices we are going to use media queries:
/* Mobile styles */
@media (max-width: 768px) {
.menu-bar {
flex-direction: column;
position: relative;
display: grid;
/* Grid layout for mobile */
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
/* Evenly divide menu items into columns */
gap: 10px;
}
.menu-item {
padding: 15px 5px;
display: block;
/* Stack icon and text vertically on mobile */
white-space: normal;
}
.menu-item {
text-align: center;
}
.menu-item i {
font-size: 2rem;
/* Larger icon for mobile */
margin-right: 0;
/* Remove margin on mobile */
display: block;
/* Icon on a separate line */
text-align: center;
}
.menu-item .shortcut {
display: none;
/* Hide shortcuts on mobile */
}
.submenu {
position: static;
display: none;
width: 100%;
background-color: #333;
top: 0;
left: 0;
z-index: 10;
}
.menu-item.active .submenu {
display: block;
}
.menu-bar.active .menu-item {
display: none;
}
.menu-bar.active .menu-item.active {
display: block;
}
.submenu .close-btn {
color: white;
padding: 10px;
cursor: pointer;
text-align: center;
background-color: #444;
}
.submenu .close-btn:hover {
background-color: #666;
}
}
and finally the javascript:
const menuData = [
{
icon: "fa-solid fa-gamepad", // Icon for "New Game..."
icon_text: "๐ฎ", // Emoji for game controller
name: "New Game...",
shortcut: "",
submenu: {
items: [
{
name: "Restart",
icon: "fa-solid fa-undo-alt", // Icon for "Restart"
icon_text: "๐", // Emoji for restart
shortcut: "Ctrl+N"
},
{
name: "Start New",
icon: "fa-solid fa-play-circle", // Icon for "Start New"
icon_text: "โถ๏ธ", // Emoji for start/play
shortcut: ""
}
]
}
},
{
icon: "fa-solid fa-cog", // Icon for "Settings..."
name: "Settings...",
icon_text: "โ๏ธ", // Emoji for settings
shortcut: "Ctrl+S",
submenu: null
},
{
icon: "fa-solid fa-th-large", // Icon for "More Solitaire..."
icon_text: "๐", // Emoji for cards/games
name: "More Solitaire...",
shortcut: "Ctrl+P",
submenu: null
}
];
function createMenu(menuData) {
const mode = "emoji";
const menuBar = document.getElementById('menuBar');
menuData.forEach(item => {
const menuItem = document.createElement('div');
menuItem.classList.add('menu-item');
// Add icon
const icon = document.createElement('i');
(mode === "emoji")
? (icon.innerText = item.icon_text, icon.style["font-style"] = "normal")
: icon.className = item.icon;
menuItem.appendChild(icon);
// Add name
const name = document.createElement('div');
name.textContent = item.name;
menuItem.appendChild(name);
// Add shortcut if available
if (item.shortcut) {
const shortcut = document.createElement('span');
shortcut.classList.add('shortcut');
shortcut.textContent = ` (${item.shortcut})`;
menuItem.appendChild(shortcut);
}
// Check for submenu
if (item.submenu) {
const submenu = document.createElement('div');
submenu.classList.add('submenu');
item.submenu.items.forEach(subItem => {
const submenuItem = document.createElement('div');
submenuItem.classList.add('submenu-item');
// Submenu item icon
const subIcon = document.createElement('i');
(mode === "emoji")
? (subIcon.innerText = subItem.icon_text, subIcon.style["font-style"] = "normal")
: subIcon.className = subItem.icon;
submenuItem.appendChild(subIcon);
// Submenu item name
const subName = document.createTextNode(` ${subItem.name}`);
submenuItem.appendChild(subName);
// Submenu item shortcut
if (subItem.shortcut) {
const subShortcut = document.createElement('span');
subShortcut.classList.add('shortcut');
subShortcut.textContent = ` (${subItem.shortcut})`;
submenuItem.appendChild(subShortcut);
}
submenu.appendChild(submenuItem);
});
// Add a close button at the end of the submenu for mobile
const closeButton = document.createElement('div');
closeButton.classList.add('submenu-item'); // Make it look like other submenu items
// Add the chevron icon
const closeIcon = document.createElement('i');
(mode === "emoji")
? (closeIcon.innerText = 'โฌ๏ธ', closeIcon.style["font-style"] = "normal")
: closeIcon.className = 'fa-solid fa-chevron-up';
closeButton.appendChild(closeIcon);
// Add the "Close" text
const closeText = document.createTextNode(' Close');
closeButton.appendChild(closeText);
closeButton.addEventListener('click', function (event) {
event.stopPropagation(); // Prevent the click from propagating to the parent .menu-item
menuItem.classList.remove('active'); // Close the submenu
});
submenu.appendChild(closeButton);
menuItem.appendChild(submenu);
// Add event listener for clicking to show/hide submenu
menuItem.addEventListener('click', function (event) {
event.stopPropagation(); // Prevent closing when clicking inside the menu
const allMenuItems = document.querySelectorAll('.menu-item');
if (window.innerWidth <= 768) {
allMenuItems.forEach(item => item.classList.remove('active')); // Close all
menuItem.classList.add('active'); // Open clicked one
} else {
menuItem.classList.toggle('active'); // Toggle submenu on desktop
}
});
// Close the submenu when clicking outside of the menu
document.addEventListener('click', function (event) {
const allMenuItems = document.querySelectorAll('.menu-item');
allMenuItems.forEach(menuItem => {
if (!menuItem.contains(event.target)) {
menuItem.classList.remove('active'); // Close the submenu if clicked outside
}
});
});
}
menuBar.appendChild(menuItem);
});
}
// Initialize the menu
createMenu(menuData);
// Add resize event listener to handle dynamic resizing
window.addEventListener('resize', function () {
const menuBar = document.getElementById('menuBar');
if (window.innerWidth > 768) {
// On desktop, ensure that all submenus are collapsed by default
const allMenuItems = document.querySelectorAll('.menu-item');
allMenuItems.forEach(item => item.classList.remove('active'));
}
});
You can play with the code here:
https://codepen.io/quantotius/pen/KKOWjyd