2
\$\begingroup\$

I've implement a custom dropdown, that help to find users from data. Each item have a userpic, first name and last name. And my task is create a dropdown with settings like use picture or not. I coded it in create function, in a loop with if-statement: if (is_use_picture) { /* ... */ }. It would be great if I could get some feedback on this code.

function Dropdown(dropdown, data, is_use_picture, is_multiselect) {

    /**
     * Reformat a data and create a dropdown.
     */

    var formatted_data;

    this.init = function() {
        formatted_data = reformat(data);

        var DROPDOWN_ITEMS_LENGTH = 10;
        var result = new Array(DROPDOWN_ITEMS_LENGTH);

        for (var i = 0; i < DROPDOWN_ITEMS_LENGTH; ++i) {
            result[i] = data[i];
        }

        create(result, is_use_picture, is_multiselect);
    };


    /**
     * @param {Array.<Object>} data.
     * @return {Array.<Object>} result.
     */

    var reformat = function(data) {
        var len = response.items.length;
        var result = new Array(len);

        var first_name;
        var last_name;
        var domain;

        for (var i = 0; i < len; ++i) {
            first_name = response.items[i].first_name.toLowerCase();
            last_name = response.items[i].last_name.toLowerCase();
            domain = response.items[i].domain.toLowerCase();

            result[i] = {
                'item_info': response.items[i],
                'first_name': first_name,
                'last_name': last_name,
                'domain': domain
            };
        }

        return result;
    };


    /**
     * Create and insert dropdown items.
     * @param {Array.<Object>} data.
     * @param {bool} is_use_picture.
     */

    var dropdown_items;
    var dropdown_items_length;

    var create = function(data, is_use_picture) {
        var item_node = document.createElement('div');
        item_node.className = 'dropdown-item cf';

        var text_node = document.createElement('span');

        if (is_use_picture) {
            var picture_node = document.createElement('span');
            picture_node.className = 'dropdown-item-picture';
        }

        var len = data.length;
        var docfrag = document.createDocumentFragment();

        var i = 0;

        while (i < len) {
            var item = item_node.cloneNode(true);
            item.setAttribute('data-index', i);
            item.setAttribute('data-id', data[i].id);
            item.addEventListener('click', select);

            if (is_use_picture) {
                var picture = picture_node.cloneNode(true);
                picture.style.backgroundImage = 'url(' + data[i].photo_50 + ')';
                item.appendChild(picture);
            }

            var text = text_node.cloneNode(true);
            text.className = 'dropdown-item-text';
            text.textContent = data[i].first_name + ' ' + data[i].last_name;
            item.appendChild(text);

            docfrag.appendChild(item);

            i++;
        }

        dropdown_items_length = i;

        clean();

        dropdown_content.appendChild(docfrag);

        dropdown_items = dropdown.querySelectorAll('.dropdown-item');
    };


    /**
     * Open/close a dropdown.
     */

    var dropdown_content = dropdown.querySelector('.dropdown-content');
    var is_open;

    var open = function() {
        dropdown_content.classList.remove('hidden');
        is_open = true;
    };

    var close = function() {
        dropdown_content.className += ' hidden';
        is_open = false;
    };

    document.addEventListener('keydown', function(event) {
        event = event || window.event;
        if (is_open && event.keyCode === 27) {
            close();
        }
    }, false);

    document.addEventListener('click', function(event) {
        event = event || window.event;
        if (is_open && event.keyCode == 0) {
            var element = event.target;

            while (element !== document) {
                if (element.className === 'dropdown') {
                    return;
                }
                element = element.parentNode;
            }

            close();
        }
    }, false);


    /**
     *
     */

    var clean = function() {
        while (dropdown_content.hasChildNodes()) {
            dropdown_content.removeChild(dropdown_content.firstChild);
        }
    };


    /**
     * Navigation by dropdown.
     */

    var UP_KEY_CODE = 38;
    var DOWN_KEY_CODE = 40;
    var SCROLL_STEP = 66;

    var keydown_count = -1;

    var hover = function(event) {
        event = event || window.event;
        if (event.keyCode === UP_KEY_CODE || event.keyCode === DOWN_KEY_CODE) {
            if (event.keyCode === UP_KEY_CODE) {
                keydown_count--;

                if (keydown_count < 0) {
                    keydown_count = dropdown_items_length - 1;
                    dropdown_content.scrollTop = SCROLL_STEP * dropdown_items_length;
                }

                if (keydown_count < (dropdown_items_length - 3)) {
                    dropdown_content.scrollTop -= SCROLL_STEP;
                }

            } else if (event.keyCode === DOWN_KEY_CODE) {
                keydown_count++;

                if (keydown_count >= dropdown_items_length) {
                    keydown_count = 0;
                    dropdown_content.scrollTop = 0;
                }

                if (keydown_count > 2) {
                    dropdown_content.scrollTop += SCROLL_STEP;
                }
            }

            for (var i = 0; i < dropdown_items_length; ++i) {
                dropdown_items[i].classList.remove('dropdown-item--hover');
            }

            dropdown_items[keydown_count].className += ' dropdown-item--hover';
        }

    };


    /**
     *
     */

    var select = function() {
        if (!is_multiselect) {
            close();
        }

        dropdown_input.value = '';

        return this.getAttribute('data-id');
    };


    /**
     *
     */

    var dropdown_input = dropdown.querySelector('.dropdown-input');

    dropdown_input.addEventListener('keydown', hover, false);

    var dropdown_input_wrap = dropdown.querySelector('.dropdown-input-wrap');

    dropdown_input_wrap.addEventListener('click', open, false);

}

I use this code to init dropdown:

var dropdown = new Dropdown(document.getElementById('dropdown'), response.items, true, false);

dropdown.init();
\$\endgroup\$
2
  • \$\begingroup\$ how would you use this function? you only seem to allow init to be called? \$\endgroup\$
    – Pinoniq
    Jul 4, 2015 at 10:12
  • \$\begingroup\$ Hi! Yes, I use init() function. I updated my question. \$\endgroup\$
    – Bob Napkin
    Jul 4, 2015 at 15:37

1 Answer 1

3
\$\begingroup\$

I'm not really sure what you're asking here, but if you're trying to go for an Object-Oriented approach here, you should be doing it like this:

function MyObject( ... ) {
    ...
}

MyObject.prototype.my_func = function( ... ) {
    ...
}

For example, your init function would become the following (assumes you've done this for your other functions/variables as well):

Dropdown.prototype.init = function() {
    this.formatted_data = this.reformat(this.data);

    var DROPDOWN_ITEMS_LENGTH = 10;
    var result = new Array(DROPDOWN_ITEMS_LENGTH);

    for (var i = 0; i < DROPDOWN_ITEMS_LENGTH; ++i) {
        result[i] = this.data[i];
    }

    this.create(result, this.is_use_picture, this.is_multiselect);
};

Also, empty comments, like the below example, should be either removed, or should have some details added to them.

/**
 *
 */
\$\endgroup\$
3
  • \$\begingroup\$ You should add more details to your answer. Can you give an actual example of how you'd rewrite part of the code using OO principles? \$\endgroup\$
    – Phrancis
    Jul 4, 2015 at 21:41
  • \$\begingroup\$ @Phrancis I've added a few more details now. \$\endgroup\$ Jul 4, 2015 at 21:46
  • \$\begingroup\$ Upvoted accordingly \$\endgroup\$
    – Phrancis
    Jul 4, 2015 at 21:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.