‘Чистый’ JavaScript: «Продолжение»

в 20:44, , рубрики: ajax, javascript, Веб-разработка, метки: ,

Иногда человечество поражает своей тупостью и нежеланием видеть ситуацию так, как она действительно есть… ( Неизвестные )

Возможно, данный эпиграф имеет ко мне большее отношение, чем я думаю, но хочется надеяться на лучшее…

Мой первый пост получился, мягко говоря, не очень, но у любого человека должна быть возможность все исправить и сейчас я попробую этим воспользоваться.

Расскажу небольшую историю, которая случилась со мной на работе. Дело было давно, как-то приасанили меня к новому web-проекту, проектированием которого я не имел ни малейшего отношения. Тимлид первым делом залил в svn php и js framework'и.
В качестве JS был выбран Mootools 1.2.2 или 1.2.4, а в дополнение к нему Jx UI Library в виде JxLib. Все шло хорошо, проект подходил к своему завершению, версии браузеров росли и тд. И вот, в один прекрасный момент, придя на работу я увидел баг, в кором говорилось а том что в IE9 (а он только-только вышел) админка не работает вообще. Первым делам начал искать проблему, как оказалось Mootools 1.2.x — не поддерживает IE9, а вот в версиях over 1.3.x — все работает и летает. Ну, долго не думая, качаем последнюю версию сего чуда и ставим. Радоватся пришлось не долго — начали вылазить баги в JxLib. Оказывается, что проект 'заглох', и последняя доступная версия совместима только с Mootools 1.2.x. Много времени прошло с того момента, но тот говнокод, с помощью которого пришлось залатать этот баг, надломал мою веру в Mootools и ему подобные вещи. Теперь, я думаю, стало ясно, откуда такое отношение к JS либам.

А теперь хочется ответить (пояснить почему так, а не иначе) на некоторые комментарии.

1)
Сonsole.log — вот этого я не ожидал, я предлогал многое но только не это. Я в курсе того что он (console.log) поддерживает форматирование и передачу нескольких параметров, но я не могу вспомнить, когда последний раз я этим пользовался. Мне вполне хватает одной переменной, а усложнять _d() ради того чтобы использовать всю мощь сonsole.log раз в месяц, — нецелесообразно (проще написать заветные 11 символов `сonsole.log`).

2)

Функция addEvent продолжит выполняться после обработки ошибки с помощью функции _d.

— я в курсе этого, это сделано специально, ибо проверку на существование DOM объекта, а предпочитаю делать в основном скрипте, но вы меня меня убедили — добавлю return false.

3)

Загрязнение глобального пространства переменных — очень плохой тон.

— тут вы правы, но кроме readyList = []; я ничего не могу найти глобального :)

Ну что ж, я думаю пора заканчивать эти холивары и начать рассмотрение новой партии JS кода.

    // Учитывая какие гуру сидят на хабре - первые 3 функции (тем более что их названия сами за себя говорят) я оставлю без комментариев
    /**
     * Function insert DOM element before some element
     *
     * @version 2012-11-06
     * @param   Object  new_element
     * @param   Object  targetElement
     * @return  void
     */
    function insertBefore(new_element, targetElement)
    {
        targetElement.parentNode.insertBefore(new_element, targetElement);
    }


    /**
     * Function insert DOM element after some element
     *
     * @version 2012-11-06
     * @param   Object  new_element
     * @param   Object  targetElement
     * @return  void
     */
    function insertAfter(new_element, targetElement)
    {
        var parent = targetElement.parentNode;

        //if the parents lastchild is the targetElement...
        if(parent.lastchild == targetElement)
            parent.appendChild(new_element);
        else
            parent.insertBefore(new_element, targetElement.nextSibling);
    }


    /**
     * Function make clone of income object
     *
     * @version 2012-11-07
     * @param   Object  obj
     * @return  Object
     */
    function clone(obj)
    {
        if(obj == null || typeof(obj) != 'object')
            return obj;

        var temp = {};
        for(var key in obj)
            temp[key] = clone(obj[key]);

        return temp;
    }


    // Функция вернет следующий DOM объект в узле, если такой существует
    /**
     * Function return next element in dom object
     *
     * @version 2013-01-08
     * @param   Object  obj
     * @return  Object
     */
    function getNext(obj)
    {
        obj = obj.nextSibling;

        if(!obj)
            return false;

        while(obj.nodeType != 1){
            obj = obj.nextSibling;

            if(!obj)
                return false;
        }

        return obj;
    }


    // Функция вернет предыдущий DOM объект в узле, если такой существует
    /**
     * Function return previous element in dom object
     *
     * @version 2013-02-13
     * @param   Object  obj
     * @return  Object
     */
    function getPrevious(obj)
    {
        obj = obj.previousSibling;

        if(!obj)
            return false;

        while(obj.nodeType != 1){
            obj = obj.previousSibling;

            if(!obj)
                return false;
        }

        return obj;
    }



    // Следующие 2 функции - это установка и чтение Cookies. C ними, я думаю, проблем не будет
    /**
     * Function new Cookies
     *
     * @version 2013-03-26
     * @param   string name
     * @param   string value
     * @param   string expires
     * @param   string path
     * @return  void
     */
    function setCookies(name, value, expires, path)
    {
        if(!name || !value)
            return 0;

        if(!expires){
            expires =  new Date();
            expires.setTime(expires.getTime() + (1000 * 86400 * 365)); //save 1 year
        }

        if(!path)
            path = '/';

        // set Cookies
        document.cookie = name+'='+escape(value)+'; expires='+expires.toGMTString()+'; path='+path;
    }


    /**
     * Function return Cookies value by name
     *
     * @version 2013-03-26
     * @param   string name
     * @return  void
     */
    function getCookies(name)
    {
        var results = document.cookie.match ( '(^|;) ?' + name + '=([^;]*)(;|$)' );

        if ( results )
            return ( unescape ( results[2] ) );
        else
            return null;
    }




    // А вот здесь придется задержатся
    // Вначале создаем функцию Ajax(params), а потом прикручиваем к ней prototype
    /**
     * Ajax object
     *
     * Send request by post/get to some url
     *
     * @version 2013-02-13
     * @param   Object  params
     * @return  Object
     */
    window.Ajax = function(params)
    {
        // параметры по умолчанию
        this.options = {
            // default url
            url: '',

            // default method
            method: 'get',

            // Is synchronous request?
            async: true,

            // in seconds 
            timeout: 10000,

            // callback function, in default - empty function
            onComplete: function(){}
        };

        // set config params
        this.setConfig(params);

        // создаем и подготавливаем кроссбраузерный XMLHttpRequest для дальнейшей работы
        // initialize
        this.init(params);
    };

    /**
     * Pablic methods
     */
    window.Ajax.prototype = {

        /**
         * some internal params
         */
        xml_http_request: null,
        timeout: null,
        // json|xml|text
        response:'json',

        // устанавливаем параметры ajax запроса, если таковы были переданы
        /**
         * configure functionality
         */
        setConfig: function(opt)
        {
            // set url
            if(opt.url != undefined)
                this.options.url = opt.url;

            // set method
            if(opt.method != undefined)
                this.options.method = opt.method;

            // set asynchronus param
            if(opt.async != undefined)
                this.options.async = opt.async;

            // set timeout
            if(opt.timeout != undefined)
                this.options.timeout = opt.timeout;

            // set callback functions
            if((opt.onComplete != undefined) && (typeof(opt.onComplete) == 'function'))
                this.options.onComplete = opt.onComplete;
        },

        /**
         * Initialize XMLHTTPRequest
         */
        init: function()
        {
            // кроссбраузерное создание XMLHttpRequest
            // Cross-browser compatibility for browsers
            if (typeof XMLHttpRequest != 'undefined') {
                this.xml_http_request = new XMLHttpRequest();
            }
            else{
                try {
                    this.xml_http_request = new ActiveXObject("Msxml2.XMLHTTP");
                }
                catch (e) {
                    try {
                        this.xml_http_request = new ActiveXObject("Microsoft.XMLHTTP");
                    } catch (E) {
                        alert('Your brouser don't support Ajax technology. Please download real browser :)');
                    }
                }
            }

            var self_ = this;

            // устанавливаем метод (post/get), url, синхронный/несинхронный запрос
            // open XMLHttpRequest
            this.xml_http_request.open(self_.options.method, self_.options.url, self_.options.async);


            /** Вешаем callback в случае успешного
             *
             *   Список состояний readyState такой: 
             *   0 - Unitialized
             *   1 - Loading
             *   2 - Loaded
             *   3 - Interactive
             *   4 - Complete
             *   Состояния 0-2 вообще не используются.
             *
             * self_.xml_http_request.status - код ответа сервера (200, 404 и тд)
             */
            // set callback function for XMLHttpRequest
            this.xml_http_request.onreadystatechange = function(){
                if((self_.xml_http_request.readyState == 4) && (self_.xml_http_request.status > 0)){


                    // отмена принудительного разрыва Ajax запроса (ниже будут пояснения)
                    // delete timeout
                    clearTimeout(self_.timeout);

                    // в завивимости от того, что вы ожидаете формируем ответ сервера
                    // JSON.parse - встроеный парсер JSON  (старые браузеры - bye-bye)
                    if(self_.response == 'json')
                        var response = JSON.parse(self_.xml_http_request.responseText);
                    else{
                        if(self_.response == 'xml')
                            var response = self_.xml_http_request.responseXML;
                        else
                            var response = self_.xml_http_request.responseText;
                    }
                    // вызываем обработчик результатов
                    self_.options.onComplete(response);
                }
            }
        },


        // Данный метод нужен в очень специфических задачах, когда необходимо играться с заголовками для сервера
        /**
         * Set some headers if need
         */
        setRequestHeader: function(name, value)
        {
            this.xml_http_request.setRequestHeader(name, value);
        },


        // Отсылаем Ajax на сервер
        /**
         * Send request
         */
        send: function(params)
        {
            this.xml_http_request.send(params);

            var self_ = this;

            // про это я говорил ранее, если вас запрос повис или на серваке проблемы - обрываем его принудительно
            // по умолчанию - 10 секунд ждем, хотя можно и больше
            // set timeout need for abort request
            this.timeout = setTimeout( function(){ self_.xml_http_request.abort(); }, this.options.timeout);
        },


        // Данная фишка для того, что бы вы могли в любое время прервать запрос, например при живом поиске (как у хабра или гугла :) )
        /**
         * Abort request
         */
        abort: function()
        {
            this.xml_http_request.abort();
        }
    }



    Пример использования Ajax():

    new Ajax({
        url: '/index.php',
        timeout: 20000,
        method: 'post',
        onComplete: function(data){ alert(data);}
    }).send('qwerty=123');


PS. Как и обещал, исходники. Статья написана для того, что бы люди не забывали истинный JavaScript и переходила на светлую сторону силы (печеньки тут тоже имеются :) ).

Автор: mamchyts

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js