Giter Club home page Giter Club logo

wafs's People

Contributors

tristanjacobs89 avatar

Stargazers

 avatar

Watchers

 avatar  avatar

Forkers

pierman1

wafs's Issues

Unnecessary second XMLHttpRequest in your getDetailsFile

Your first XMLHttpRequest in doApiCall returns 25 objects with information you can use in the gif-details page. No need to do a second API call just to retrieve these information again.

It's too much to explain but basically you can save the data that you need for each gif file in the app var in a seperate app.date object.

I've refactored the code:

(function() {
    "use strict";

/* UTILITIES
========================================================================= */

    var utils = {
        $: function(id) {
            return document.querySelector(id);
        },
        $$: function(el) {
            return document.querySelectorAll(id);
        }
    };

/* DOM ELEMENTS
========================================================================= */

    var elements = {
        sections: {
            $details:           utils.$("#details"),
            $img_container:     utils.$('#img-container'),
            $img_source:        utils.$('#img-source'),
            $img_post_date:     utils.$('#img-posted-on'),
            $start:             utils.$('#start'),
            $result_area:       utils.$('#results'),
            $error:             utils.$('#error')
        },

        form: {
            $self:              utils.$('#search-form'),
            $search_input:      utils.$('#search-input'),
            $gif_selector:      utils.$('#gif-selector'),
            $sticker_selector:  utils.$('#sticker-selector'),
            $submit_button:     utils.$('#submit'),
            $spinner:           utils.$('#spinner')
        },

        toggleSpinner: function() {
            elements.form.$spinner.classList.toggle('hidden');
            elements.form.$submit_button.classList.toggle('hidden');
        }
    };

/* ROUTES
========================================================================= */

    var routes = {
        init: function() {
            routie({

                // Set default route to #start
                '': function() {
                    location.hash = '#start';
                },
                // Start route
                'start': function() {
                    elements.sections.$start.classList.remove("hidden");
                    elements.sections.$details.classList.add("hidden");

                },
                // Details route
                'details/:id': function() {
                    elements.sections.$start.classList.add("hidden");
                    elements.sections.$details.classList.remove("hidden");
                    app.getDetailsAndRender();
                }
            });
        }
    };

/* MAIN APP
========================================================================= */
    var app = {

        /* Initializer
        ===================== */

        init: function() {
            routes.init();

            elements.form.$self.addEventListener('submit', function(e) {
                // Prevent page from reloading on submit
                e.preventDefault();

                // Get the url parameters
                var query_type  = app.getQueryType(),
                    query       = app.getSearchQuery(),
                    urlForApi   = api_config.URL + query_type + "/search?q=" + query + "&api_key=" + api_config.KEY;

                // Get data from the API
                app.doApiCall(urlForApi);
            });
        },

        /* Data retrieved by api cal
        ===================== */

        data: {},

        /* Call to API
        ===================== */

        doApiCall: function(url) {

            // Empty the result area
            elements.sections.$result_area.innerHTML = '';
            // Toggle the loading spinner
            elements.toggleSpinner();

            // Set a 1 second delay on the API call for perceived performance
            setTimeout(function() {
                var request = new XMLHttpRequest();
                request.open('GET', url, true);

                // empty the data object
                app.data = {};


                request.onload = function() {
                    if (request.status >= 200 && request.status < 400) {
                        // Use reduce to filter out only the needed information from the returned data
                        var response = JSON.parse(request.responseText).data.reduce(
                          function(accumulator, currentValue) {
                            accumulator.push({
                              id: currentValue.id,
                              image: currentValue.images.preview_webp,
                            });

                            // populate app.data with values for the gif-info page
                            app.data[currentValue.id] = {
                              bigImg: currentValue.images.downsized_large.url,
                              postDate: currentValue.import_datetime,
                              source: currentValue.source_post_url
                            }

                            return accumulator;

                          // Sort the data by their ID's
                        }, []).sort(function(a, b) {
                          return a.id - b.id;
                        });

                        // Check if the response object has data
                        if (response.length == 0) {

                          console.log('No data..');
                          elements.toggleSpinner();

                          // Show error message on page if there is no data
                          elements.sections.$error.classList.remove('hidden');

                        } else {

                          elements.sections.$error.classList.add('hidden');
                          console.log('Data found!');
                          elements.toggleSpinner();

                          // Call the render data function to show images
                          app.renderData(response);
                        }
                    } else {
                        console.log("Error!");
                    }
                };
                request.onerror = function(err) {
                    console.log('Error making connection: ' + err);
                };
                request.send();
            }, 1000)
        },

        /* Get the query type (stickers or gifs)
        ===================== */

        getQueryType: function() {

          var queryType;

            // Set the query type to "gifs" if gifs checkbox is selected
            if (elements.form.$gif_selector.checked) {
                queryType = "gifs";
                console.log('User selected GIFS');
                return queryType;

                // Set the query type to "stickers" if stickers checkbox is selected
            } else if (elements.form.$sticker_selector.checked) {
                queryType = "stickers";
                console.log('User selected STICKERS');
                return queryType;
            }
        },

        /* Get the search query
        ===================== */

        getSearchQuery: function() {
            var query = elements.form.$search_input.value;
            return query;
        },

        /* Render data on page
        ===================== */

        renderData: function(results) {
            results.map(function(item) {

                var img_link  = item.image.url;
                var item_id   = item.id;

                window.scroll(0, 300);
                elements.sections.$result_area.innerHTML +=
                '<a class="animated fadeIn" href="#details/' + item_id + '">' +
                    '<img src="' + img_link + '"/>' +
                '</a>';
            });
        },

        /* Get details of selected image
        ===================== */

        getDetailsAndRender: function() {
            var hashRoute = location.hash;
            var id        = hashRoute.slice(9);
            var gifObj    = app.data[id];

            // get data for the gif-element from app.data object
            var bigImg    = gifObj.bigImg;
            var postDate  = gifObj.postDate;
            var source    = gifObj.source;

            elements.sections.$img_container.innerHTML  = '<img class="detail-img" src="' + bigImg + '">';
            elements.sections.$img_source.innerHTML     = '<a href="'+ source + '" target="_blank">' + source + '</a>';
            elements.sections.$img_post_date.innerHTML  = postDate;

        }
    };
    app.init();
})();

Uncompressed backgroud image

The background image in your spa is almost 4 mb and takes 10 seconds to load on a speedy connection. Please compress the image

Don't just return user provided input value

Always check user input before making a call to the server. Now, if I push spacebar into the searchfield (thus, technically not empty), a call is made to the server but it's an unnecessary call.

        getSearchQuery: function() {
            var query = elements.form.$search_input.value;
            return query;
        },

Double var declaration

There is no lexical scoping in If-statements, so no need to declare a var 2 times.

// Set the query type to "gifs" if gifs checkbox is selected
            if (elements.form.$gif_selector.checked) {
                var queryType = "gifs";
                console.log('User selected GIFS');
                return queryType;

                // Set the query type to "stickers" if stickers checkbox is selected
            } else if (elements.form.$sticker_selector.checked) {
                var queryType = "stickers";
                console.log('User selected STICKERS');
                return queryType;
            }

Instead do:

            var queryType;
            
            // Set the query type to "gifs" if gifs checkbox is selected
            if (elements.form.$gif_selector.checked) {
                queryType = "gifs";
                console.log('User selected GIFS');
                return queryType;

                // Set the query type to "stickers" if stickers checkbox is selected
            } else if (elements.form.$sticker_selector.checked) {
                queryType = "stickers";
                console.log('User selected STICKERS');
                return queryType;
            }

App needs more structure

Separate logic into object literals and try to keep your methods lean and mean (as little code as possible per method)

Function declarations on top and at the bottom

Because you're using function declarations (difference in hoisting vs function expressions), you can order them at the bottom of the file for better structure and readability.

// code...

// bottom of file ...
function $id(id) { ... }
function getSearchResults(filter)  { ... }

Lean and mean HTML

Don't use unnecessary container

elements. Try to keep your html as lean and mean as possible.

Better handling of server output

When you search for "dog eat", the server responds a second gif object where somehow the image object is undefined and thus preventing the rest of the gifs to load.

schermafbeelding 2017-05-17 om 16 06 18

I've updated the code like this:

// this
var img_link  = item.image.url;
var item_id   = item.id;

// to
var img_link  = item.image ? item.image.url : "";
var item_id   = item.id;

You can see now in the console that indeed the second gif object has an undefined image property instead of an object. But the rest is loaded like it should.
schermafbeelding 2017-05-17 om 16 10 31

Use a predefined selectvalue.

If user doesn't select either the sticker or gif radio button, the server responds with a 404 error that is not handled by the app. Instead the spinner keeps on spinning.

XMLHttpRequest cannot load https://api.giphy.com/v1/undefined/search?q=cats&api_key=dc6zaTOxFJmzC&limit=25. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 404.

Solution: either preselect either 'gif' or 'sticker' radio button to prevent this error or refactor your formElements.selectedFilter() function that can handle the exception when none of the radiobuttons is selected.

Unused function $$(id)

Following function is unused in your app.js file.

    function $$(id) {
        return document.querySelectorAll(id);
    }

Placement <script> tag

Place <script> tag above the closing tag underneath all other elements. Or place it in the section with a defer attribute

Keep app.js only for the app function

Relocate utils, elements and routes to its own files for better structure, like you did with the apiconfig.js file.

I would even go further and move each method in your app object to a different file. But thats just a personal style and I also don't always do it myself.

strict mode

Are you sure you want to run strict mode on the global scope?

Wrong param passed in function

See $$ method.

var utils = {
        $: function(id) {
            return document.querySelector(id);
        },
        $$: function(el) {
            return document.querySelectorAll(id);
        }
    };

Use better error handling

At this moment, when server responds with a 400+ error, the app just console.logs "Error!". Make better use of the errors received by the server for your users so they know what's going on when something goes wrong.

Thus refactor your request.onload method, especially this line:

else {
    console.log("Error!");
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.