HTML5 Zone is brought to you in partnership with:

I was born in 1981 in one little city. Since I was 10y/o I programmed in different languages. My first languages were basic, then C++/MFC, after .Net (C#, VB.Net, J#, ASP.Net), XSL+XML processing). In the last 5 years I worked with web languages (HTML, CSS, PHP, SQL, XML, XSL, JavaScript). After university I worked in several different companies, eventually becoming a blogger. This is my hobby too. Andrey is a DZone MVB and is not an employee of DZone and has posted 111 posts at DZone. You can read more from them at their website. View Full User Profile

Bottom Menu Builder (HTML5)

05.22.2012
| 3443 views |
  • submit to reddit

I am sure that you have seen this before (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 which is user interface with drag and drop possibility.

As first I suggest you download the source files and keep the demo opened in a tab for better understanding.

 

Live Demo

 

download result

 

So, let's 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

/* 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;
}

Now, it’s time to style our menu builder page:

css/main.css

 

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. While 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 learn our builder to generate results.

 

Live Demo

 

download result

 

Published at DZone with permission of Andrey Prikaznov, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)