1
\$\begingroup\$

I have been working on a jQuery Mobile (1.4.5) application for the past week.

In short, the application is a bunch of pages that show listings of beers, breweries, and other things.

There are few pages that have forms. All except one, these pages are using the jQuery Validator plugin for validating the fields. This one page is for rating a beer which also contains a map using Google API.

Everything works fine but the problem I am facing now is: how can I clean up my single JavaScript file, which contains all my variables, functions, page handlers? Preferably I would like to break up my code into several files, but I am not sure how. I am also polluting my global namespace with all these variables and functions, is there a way to fix that? Any other critiques/remarks is also welcome.

Below is a sample of my code. I have removed a lot of things in this demonstration to not cause too much headaches.

var beers = null;
var breweries = null;

var siteUrl = 'http://localhost/mystore';

var map;
var coords = { latitude: 43.6481, longitude: -79.4042 };
var markers = [];
var icons = {
    typeA: { src: siteUrl + '/images/pin_purple.png', width: 41, height: 39 },
    typeB: { src: siteUrl + '/images/pin_red.png', width: 41, height: 39 },
    typeC: { src: siteUrl + '/images/pin_blue.png', width: 41, height: 39 },
    location: { src: siteUrl + '/images/dot_blue.png', width: 38, height: 38 }
};
var infowindow;


(function($) {

    $.validator.setDefaults({
        errorClass: 'jqv-error',
        onfocusout: false,
        onkeyup: false,
        onclick: false
    });

})(jQuery);


$(document).on('pagecreate', '#sign-in', function() {
    var validator = $('#s-form').validate({ 
        rules: {
            's-email': { 
                required: true, 
                email: true 
            },
            's-password': { 
                required: true 
            }
        },
        messages: {
            's-email': { 
                required: 'Enter your email address.', 
                email: 'Invalid email address.' 
            },
            's-password': { 
                required: 'Enter your password.' 
            }
        },
        errorPlacement: function(error, $element) {
            $element.parent().after(error);
        },
        submitHandler: function(form) {
            post('json/ajaxHandler.php?action=sign-in', $(form).serialize(), function(data) {
                if (data.success) {
                    form.reset();
                    changePage('welcome.php');
                } else {
                    validator.showErrors({ 's-password': 'The email or password you entered is incorrect.' });
                }
            });
            return false;
        }
    });
});

$(document).on('pagecreate', '#register', function() {
    // similar to block of code as  'pagecreate' #sign-in
});

$(document).on('pagecreate', '#forgot-password', function() {
    // similar to block of code as  'pagecreate' #sign-in
});

$(document).on('pagecreate', '#rate-beer', function() {
    var $criterias = $('#criterias input');

    $criterias.on('slidestop', function(e) {
        var average = 0, sum = 0, count = 0;
        // calculate average of all the sliders with a value
        $criterias.each(function() {
            if (this.value !== '') {
                sum += Number(this.value);
                count++;
            }
        });
        if (count) {
            average = (Math.round(sum / count * 2) / 2).toFixed(1);
        }
        // display average
        $('#rating').val(average);  
    });
    // submit form
    $('#form-rating').on('submit', function(e) {
        e.preventDefault();

        post('json/ajaxHandler.php?action=rate-beer', $(this).serialize(), function(data) {
            if (data.success) {
                changePage('#beers');
            }
        });
    });
    // refresh a map that is contained inside a collapsible
    $('#collapsible-map').on('collapsibleexpand', refreshMap);
    // show/hide markers when a certain button is clicked
    $('#map-legend button').on('click', toggleMarkers);

    drawMap();
});

// bind events and retrieve data from the database once
$(document).on('pagecreate', '#beers', function() {
    bindBeersEvents();
    getBeers();
});
$(document).on('pagecreate', '#breweries', function() {
    bindBreweriesEvents();
    getBreweries();
});


$(document).on('pagecontainerbeforeshow', function(e, ui) {
    var toPage = ui.toPage[0].id;
    // before any listing pages are shown, refresh the list 
    // (without making any call to the database)
    if (toPage == 'beers') {
        populateBeers();
    }
    if (toPage == 'breweries') {
        populateBreweries();
    }
});

function bindBeersEvents() {
    // similar to bindBreweriesEvents() below
    // it is used used to attach event handlers to elements on specific pages
}

function bindBreweriesEvents() {
    $('#select-breweries').on('change', openCollapsible);
}

function getBeers() {
    // similar to getBeers() below
}

function getBreweries() {
    post('json/ajaxHandler.php?action=get-breweries', null, function(data) {
        if (data.success) {
            breweries = data.breweries;
            populateBreweries();
        }
    });
}

function populateBeers() {
    // similar to populateBreweries() below
}

function populateBreweries() {
    // build select & collapsibleset
    $.each(breweries, function(name, beers) {
        options += '<option value="' + name + '">' + name + '';
        list += '<div data-role="collapsible">';
        list += '<h3>' + name + '</h3>';
        list += '<ul data-role="listview">';
        $.each(beers, function() {
            list += '<li><a href="brewery.php?brewery=' + this.id + '">';
            list += '<img src="' + this.name + '" class="ui-li-icon" width="30" height="30">';
            list += '</a></li>';
        });
        list += '</ul></div>';
    }); 
    // refresh elements
    $('#select-breweries').html(options).selectmenu('refresh');
    $('#collapsibleset-breweries').html(list).enhanceWithin();
}

function openCollapsible() {
    // expand a given collapsible
}


function drawMap() {
    map = new google.maps.Map(document.getElementById('map-canvas'), {
        backgroundColor: 'transparent',
        center: new google.maps.LatLng(coords.latitude, coords.longitude),
        mapTypeControl: false,
        maxZoom: 17,
        minZoom: 6,
        panControl: false,
        scaleControl: false,
        streetViewControl: false,
        zoom: 6,
        zoomControl: true,
        zoomControlOptions: {
            position: google.maps.ControlPosition.TOP_RIGHT
        } 
    });
    infowindow = new google.maps.InfoWindow({
        maxWidth: 250
    });

    google.maps.event.addListenerOnce(map, 'tilesloaded', getMarkers); // ready
    google.maps.event.addListenerOnce(map, 'resize', fitMarkers);
    google.maps.event.addListener(map, 'click', closeInfobox);

    getRetailers();
}

function refreshMap() {
    // ...
}

function getRetailers() {
    // similar to the other get___() functions above
    // make AJAX request to retrieve JSON object
    // once JSON object is given, call setMarkers() while passing the object
}

function setMarkers(retailers) {
    // create marker for current location
    var markerL = new google.maps.Marker({
        _distance: 0,
        _type: 'location',
        icon: {
            url: icons['location'].src,
            size: new google.maps.Size(icons['location'].width, icons['location'].height),
            origin: new google.maps.Point(0,0)
        },
        map: map,
        position: map.getCenter()
    });
    markers.push(markerL);
    // create markers for the retailers 
    $.each(retailers, function(i, retailer) {
        var marker = new google.maps.Marker({
            _distance: parseFloat(retailer.distance),
            _type: retailer.type,
            icon: {
                url: icons[retailer.type].src,
                size: new google.maps.Size(icons[retailer.type].width, icons[retailer.type].height),
                origin: new google.maps.Point(0,0)
            },
            map: map,
            position: new google.maps.LatLng(retailer.lat, retailer.lng)
        }); 
        google.maps.event.addListener(marker, 'click', function() {
            infowindow.setContent(
                '<div id="iw-container">' +
                    '<div class="iw-title iw-title-' + retailer.type + '">' + retailer.name + '</div>' +
                    '<div class="iw-content">' + retailer.address + '</div>' +
                '</div>'
            );
            infowindow.open(map, marker);
        });

        markers.push(marker);
    });
}

function fitMarkers() {
    // set the viewport to contain the bounds for the first 10 markers
}

function toggleMarkers() {
    // show/hide markers
}

function closeInfobox() {
    infowindow.close();
}



function changePage(url) {
    $(':mobile-pagecontainer').pagecontainer('change', url, { showLoadMsg: true });
}

function loadPage(url) {
    $(':mobile-pagecontainer').pagecontainer('load', url);
}

function post(url, data, success){
    return $.ajax({
        type: 'post',
        url: url, 
        data: data, 
        cache: false,
        crossDomain: true,
        dataType: 'json',
        success: success
    });
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.