Bottom Menu Builder (HTML5)
Bottom navigation website menu. I am sure that you have seen it many times (at different websites). As usual, this is three-four columns menu with different links. Today I would like to show you a builder, which you can use to build that bottom menu. Main purpose of this builder – prepare static HTML code (as cache file) for embedding into bottom of your website. If you like that idea, you are welcome to play with it, and I will tell you about creation of nice user friendly menu builder. This tutorial is separated into 2 parts, today I will tell you about first side: user interface with drag and drop possibility.
As the first, I would suggest you to download the source files and keep the demo opened in a tab for better understanding.
Live Demo
download result
So, lets start
Step 1. HTML
index.html
<div class="actions"> Actions: <button id="preview" disabled>Preview</button> <button id="add_col">Add Column</button> </div> <div class="actions">Columns (with active elements)</div> <div class="columns"> <div class="column" id="drop_1" droppable="true"><img src="images/delete.png" onclick="removeColumn(this)" /></div> <div class="column" id="drop_2" droppable="true"><img src="images/delete.png" onclick="removeColumn(this)" /></div> <div class="column" id="drop_3" droppable="true"><img src="images/delete.png" onclick="removeColumn(this)" /></div> </div> <div style="clear:both"></div> <div class="actions">All (inactive) elements. You can drag these elements into columns.</div> <div class="inactive" droppable="true"> <a id="1" draggable="true">Link 1</a> <a id="2" draggable="true">Link 2</a> <a id="3" draggable="true">Link 3</a> <a id="4" draggable="true">Link 4</a> <a id="5" draggable="true">Link 5</a> <a id="6" draggable="true">Link 6</a> <a id="7" draggable="true">Link 7</a> <a id="8" draggable="true">Link 8</a> <a id="9" draggable="true">Link 9</a> <a id="10" draggable="true">Link 10</a> <a id="11" draggable="true">Link 11</a> <a id="12" draggable="true">Link 12</a> </div> <script src="js/main.js"></script>
There are three main sections: block with actions, block with active columns and block with inactive elements. All the elements we can drag and drop between columns. Also we can add and remove our columns.
Step 2. CSS
Now, it’s time to style our menu builder page:
css/main.css
/* menu builder styles */ .actions { border: 1px solid #CCCCCC; font-size: 24px; margin: 20px auto 5px; overflow: hidden; padding: 10px; width: 900px; /* CSS3 Box sizing property */ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -o-box-sizing: border-box; box-sizing: border-box; } .actions button { cursor: pointer; font-size: 20px; padding: 5px; } .actions #add_col { float: right; } .inactive { border: 1px dashed #ccc; margin: 0 auto; width: 900px; } .inactive a { border-color: #FFFFFF; border-style: solid; border-width: 8px 8px 20px; cursor: pointer; display: inline-block; font-size: 20px; height: 20px; margin: 10px; opacity: 1; position: relative; text-align: center; width: 180px; -khtml-user-drag: element; /* CSS3 Prevent selections */ -moz-user-select: none; -webkit-user-select: none; -khtml-user-select: none; user-select: none; /* CSS3 Box Shadow */ -webkit-box-shadow: 2px 2px 4px #444; -o-box-shadow: 2px 2px 4px #444; box-shadow: 2px 2px 4px #444; } .inactive a.hidden { height: 0; margin: 0; opacity: 0; width: 0; } .columns { margin: 0 auto; overflow: hidden; width: 900px; } .column { border: 2px dashed #ccc; float: left; min-height: 100px; padding: 10px; position: relative; width: 33.3%; /* CSS3 Box sizing property */ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -o-box-sizing: border-box; box-sizing: border-box; } .column a { border-color: #FFFFFF; border-style: solid; border-width: 4px 4px 10px; cursor: pointer; display: block; font-size: 16px; height: 30px; margin-bottom: 15px; opacity: 1; position: relative; text-align: center; -khtml-user-drag: element; -webkit-user-drag: element; /* CSS3 Prevent selections */ -moz-user-select: none; -webkit-user-select: none; -khtml-user-select: none; user-select: none; /* CSS3 Box Shadow */ -webkit-box-shadow: 2px 2px 4px #444; -o-box-shadow: 2px 2px 4px #444; box-shadow: 2px 2px 4px #444; /* CSS3 Box sizing property */ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -o-box-sizing: border-box; box-sizing: border-box; } .column img { cursor: pointer; position: absolute; right: 2px; top: 2px; z-index: 5; }
Step 3. JS
js/main.js
// add event handler realization var addEvent = (function () { if (document.addEventListener) { return function (el, type, fn) { if (el && el.nodeName || el === window) { el.addEventListener(type, fn, false); } else if (el && el.length) { for (var i = 0; i < el.length; i++) { addEvent(el[i], type, fn); } } }; } else { return function (el, type, fn) { if (el && el.nodeName || el === window) { el.attachEvent('on' + type, function () { return fn.call(el, window.event); }); } else if (el && el.length) { for (var i = 0; i < el.length; i++) { addEvent(el[i], type, fn); } } }; } })(); // update handlers for draggable objects function updateHandlerDrag() { var dragItems = document.querySelectorAll('[draggable=true]'); for (var i = 0; i < dragItems.length; i++) { // dragstart event handler addEvent(dragItems[i], 'dragstart', function (event) { event.dataTransfer.setData('obj_id', this.id); return false; }); } } // update handlers for droppable objects function updateHandlerDrop() { var dropAreas = document.querySelectorAll('[droppable=true]'); // dragover event handler addEvent(dropAreas, 'dragover', function (event) { if (event.preventDefault) event.preventDefault(); this.style.borderColor = "#000"; return false; }); // dragleave event handler addEvent(dropAreas, 'dragleave', function (event) { if (event.preventDefault) event.preventDefault(); this.style.borderColor = "#ccc"; return false; }); // dragenter event handler addEvent(dropAreas, 'dragenter', function (event) { if (event.preventDefault) event.preventDefault(); return false; }); // drop event handler addEvent(dropAreas, 'drop', function (event) { if (event.preventDefault) event.preventDefault(); // get dropped object var iObj = event.dataTransfer.getData('obj_id'); var oldObj = document.getElementById(iObj); // get inner text var linkText = oldObj.innerHTML; oldObj.className += 'hidden'; // remove object from DOM oldObj.parentNode.removeChild(oldObj); // add similar object in another place this.innerHTML += '<a id="'+iObj+'" draggable="true">'+linkText+'</a>'; // and update event handlers updateHandlerDrag(); this.style.borderColor = "#ccc"; return false; }); } // add column button var addColBtn = document.querySelectorAll('#add_col'); addEvent(addColBtn, 'click', function (event) { if (event.preventDefault) event.preventDefault(); // recalculate widths for columns var oCols = document.querySelector('div.columns'); var iChilds = oCols.childElementCount + 1; var dWidth = 100 / iChilds; // add single column oCols.innerHTML += '<div class="column" id="drop_'+(iChilds+1)+'" droppable="true"><img src="images/delete.png" onclick="removeColumn(this)" /></div>'; // set new widths for (var i = 0; i < iChilds; i++) { oCols.children[i].style.width = dWidth + '%'; } // update handlers updateHandlerDrop(); return false; }); // remove columns function removeColumn(obj) { var oParent = obj.parentNode; // move object to inactive area var oInactive = document.querySelector('div.inactive'); for (var i = 0; i < oParent.childNodes.length; i++) { if (oParent.childNodes[i].nodeType == document.ELEMENT_NODE && oParent.childNodes[i].tagName == 'A') { oInactive.innerHTML += '<a id="'+oParent.childNodes[i].id+'" draggable="true">'+oParent.childNodes[i].innerHTML+'</a>'; } } // remove column oParent.parentElement.removeChild(oParent); // recalculate widths for columns var oCols = document.querySelector('div.columns'); var iChilds = oCols.childElementCount; var dWidth = 100 / iChilds; // set new widths for (var i = 0; i < iChilds; i++) { oCols.children[i].id = 'drop_' + (i + 1); oCols.children[i].style.width = dWidth + '%'; } // update handlers updateHandlerDrop(); updateHandlerDrag(); } // update handlers updateHandlerDrag(); updateHandlerDrop();
There are a lot of event handlers (in order to make that html5 drag and drop). In the beginning, the script updates different handlers for all draggable and dropable objects. During dragging between blocks (drop areas) we should recreate objects and update handlers. In case when we need to remove column, we should move all the object of active column back to inactive area (with list of all possible elements). Tomorrow we will teach our builder to generate results.
Live Demo
download result
Conclusion
That’s all, today we have implemented first half of our bottom menu builder with native Drag and Drop functionality. Hope that our tutorial has helped you. Feel free to share our tutorials with your friends. Good luck!