Ответы пользователя по тегу JavaScript
  • Don't make functions within a loop (как убрать ошибку)?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    function getSomeHtml() {
            return $(this).html();
    }
    
    for (var p = 0, len = number.length; p < len; p++) {
            if (number[p] == 'Свободен') {
                number_js[p] = $('.ofice:eq(' + p + ')>.ofice_number>strong').map(getSomeHtml)[0];
    
                for (var i = 0, len = rsrGroups.length; i < len; i++) {
                    if (number_js[p] == rsrGroups[i].data("room")) {
                        rsrGroups[i].attr('fill', '#F4DA6C');
                    }
                }
            }
        }


    Только функция тут вообще не нужна:
    number_js[p] = $('.ofice:eq(' + p + ')>.ofice_number>strong').first().html();
    Ответ написан
    22 комментария
  • Как отсортировать div элементы?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Может быть можно как то элегантнее решить данную задачу?
    Разумеется, можно. Достаточно понять, что вам нужно не строки таблицы сортировать, а данные. И уже данные потом рендерить.

    Данные должны контролировать DOM, а не наоборот. Такой подход позволит не только существенно ускорить работу приложения (избавляемся от ненужных операций с DOM-элементами), но и сделает код более понятным и расширяемым.

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

    Посмотрите, например, как просто, а главное понятно, ваша задача решается при помощи Backbone. Приложение разделено на слои и разбито на части, каждый слой и каждая часть занимаются своим делом. Само собой, это quick'n'dirty демо, там могут быть ошибки, так что не стоит использовать этот код в продакшене :)
    Ответ написан
    2 комментария
  • Можно простыми словами объяснить принцип работы метода Apply в Java Script?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    methods.init.apply(this, arguments);
    Здесь метод init объекта methods вызывается c переопределенным контекстом this и в него передаются все аргументы, которые были переданы в функцию easyFader.

    Проще, вроде, некуда.
    Ответ написан
    Комментировать
  • Существуют ли сниппеты комментариев для PhpStorm как в SublimeText3?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Вводите над определением функции /**, нажимаете Enter - вуаля, DocBlock готов!
    Документация
    Ответ написан
  • Douglas Crockford или Flanagan D для изучения JavaScript?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Все зависит от того, что вы вкладываете в понятие "разобраться". Я бы рекомендовал прочитать учебник Ильи Кантора, скорее всего, этого будет достаточно.
    Ответ написан
    5 комментариев
  • Как понять эту матрицу?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    You may also pass in a matrix while instantiating the PF.Grid class. It will initiate all the nodes in the grid with the same walkability indicated by the matrix. 0 for walkable while 1 for blocked.

    Как обычно, ответ на ваш вопрос находится в документации.

    Эта матрица, по сути - карта вашего поля, где 0 означает, что по клетке можно пройти, а 1 - нельзя.
    Если вам нужно получить информацию о проходимости какой-то конкретной клетки, то это сделать довольно легко:
    matrix [координата Y (номер строки)] [координата X (номер колонки)]
    Ответ написан
    3 комментария
  • Что не так в Javascript?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    document.querySelector("#nav-toggle")
    На странице нет такого элемента, поэтому querySelector возваращает null.
    Об этом в ошибке и написано - у null нет метода addEventListener.
    addEventListener вызывается только в одном месте - правда, разобраться совсем не сложно.

    А вообще, если уж используете jquery, так и используйте на полную катушку:
    if ($(window).width() > 991) {
        $("#nav-toggle").on("click", function() {
            $(this).toggleClass("active");
        });
    }
    Ответ написан
    3 комментария
  • Как можно улучшить код на JQuery?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Я же вам здесь очень подробно ответил.
    Принципы, которые там описаны, применяются одинаково хорошо и для ванильного js и для jquery.

    var classes = ['item-id', 'item-title', 'item-price', 'delete-btn'];
    
    $('#sf tr').each(function () {
        var $cell = $('<td>').append('<a class="add_item">Добавить в корзину</a>');
        
        $(this).append($cell);
    
        $(this).find('td').each(function (index, element) {
            $(element).addClass(classes[index]);
        });    
    });


    Или вот такой вариант, без итерации по строкам:
    var classes = ['item-id', 'item-title', 'item-price', 'delete-btn'],
        $rows = $('#sf tr').filter(':gt(0)'),
        $link = $('<a>').addClass('add_item').text('Добавить в корзину'),
        $cell = $('<td>').append($link),
        $cells;
    
    $rows.append($cell);
    
    $cells = $rows.find('td');
    
    $cells.each(function (index, cell) {
        $(cell).addClass(classes[index % classes.length]);
    });


    Интересные, кстати, получаются результаты сравнения скорости работы этих двух вариантов.
    Ответ написан
    9 комментариев
  • Почему постоянно лезет undefined?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    На сколько я поля это фишка асинхронности !?
    Правильно поняли.

    Это получается нужно код выстраивать цепочкой из калбэков ?
    Необязательно. (для nodejs, понятно, своя специфика, но общие принципы такие же, естественно).
    Ответ написан
    Комментировать
  • Как выполнять функцию по завершении часто повторяющегося события, а не много раз подряд?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Дело в том, что за время вращения колеса мыши, событие mousewheel срабатывает много раз. Соотвественно и обработчик события срабатывает столько же раз.
    Чтобы этого избежать, нужно применить debounce обработчика, то есть сделать так, чтобы он выполнился один раз через n миллисекунд после его последнего вызова. Если с момента последнего вызова не прошло n миллисекунд, а обработчик был вызван еще раз, его выполнение откладывается еще на n миллисекунд.
    В данном случае, обработчик выполнится через 100 миллисекунд после того, как перестанут поступать события скролла (100 - это просто пример, реальное значение нужно подбирать исходя из ситуации):
    $(document).ready(function () {
        var owl = $(".sldr").owlCarousel().data('owlCarousel');
    
        $(window).on('mousewheel', debounce(function (event) {
            var direction = event.originalEvent.deltaY < 0 ? 'next' : 'prev';
            owl[direction]();
        }, 100));   
    });
    Код функции debounce
    function debounce(func, wait, immediate) {
        var timeout, args, context, timestamp, result;
    
        var later = function () {
            var last = new Date().getTime() - timestamp;
    
            if (last < wait && last >= 0) {
                timeout = setTimeout(later, wait - last);
            } else {
                timeout = null;
                if (!immediate) {
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                }
            }
        };
    
        return function () {
            context = this;
            args = arguments;
            timestamp = new Date().getTime();
            var callNow = immediate && !timeout;
            if (!timeout) timeout = setTimeout(later, wait);
            if (callNow) {
                result = func.apply(context, args);
                context = args = null;
            }
    
            return result;
        };
    }

    Реализацию функции debounce я беззастенчиво позаимствовал из библиотеки underscore.
    Интерактивный пример
    Ответ написан
    Комментировать
  • Как оптимизировать данный код (открытие/закрытие всплывающих окон)?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    У вас по сути N систем из трех взаимосвязанных элементов.
    Сами системы между собой никак не связаны, а вот элементы внутри системы между собой нужно как-то связать.
    Самый простой вариант - использовать индексы (интерактивный пример):
    var buttons = document.querySelectorAll('.js-btn-item'),
        popups = document.querySelectorAll('.js-products-popup'),
        closers = document.querySelectorAll('.js-btn-close'),
        index, button, popup, closer;
    
    for (index = 0; index < buttons.length; index++) {
        button = buttons[index];
        popup = popups[index];
        closer = closers[index];
    
        button.addEventListener('click', buttonHandler(popup));
        closer.addEventListener('click', closerHandler(popup))
    }
    
    function buttonHandler(popup) {
        return function (event) {
            event.preventDefault();
            popup.classList.add('js-popup-show');
        }
    }
    
    function closerHandler(popup) {
        return function (event) {
            event.preventDefault();
            popup.classList.remove('js-popup-show');
        }
    }

    Объяснение, зачем мы делаем такие сложные обработчики событий из двух вложенных функций, находится здесь. В современных браузерах можно сделать проще, но при этом внутри обработчика события this будет ссылаться уже не на элемент, а на что-то другое (скорее всего, window), но в данном случае это не имеет значения.

    Для того, чтобы этот вариант работал, естественно, элементы каждого типа должны идти в строго одинаковом порядке.
    Чтобы этого избежать, можно использовать родителя-обертку, внутри которого содержатся все 3 элемента системы (пример). Такой подход, помимо всего прочего, за счет делегирования позволяет устанавливать N обработчиков, а не N*2, как в первом случае. Но код обработчика слегка усложняется - добавляется проверка целевого элемента.

    Третий способ показал Алексей Зуев - он более декларативный, как видите, javascript-кода там совсем немного - одна универсальная функция. Но этот вариант требует использования id вместо классов, это не всегда возможно.
    Ответ написан
    Комментировать
  • Подключение/отключение CSS стилей по кнопке, с прямым изменением в файле css?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега PHP
    Как вам уже сказали, это серверная логика, для этого придется написать php-скрипт.
    При нажатии на кнопку отправлять ajax-запрос на этот скрипт (ну или просто переходить на него, а в конце редиректить куда нужно), а в самом скрипте изменять css-файл и перезаписывать его. Проще всего, разумеется, воспользоваться готовыми библиотеками.
    Ответ написан
    1 комментарий
  • Как отменить событие после его выполнения?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    $(e).one('click', function () {
      в следующий раз не вызывать эту функцию при клике на $(e)
    })

    Документация
    Ответ написан
    Комментировать
  • Owlcarousel 1.3. Как получить индекс активного слайда?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Ведь прямо на той странице написано:
    updateResult(".currentItem", this.owl.currentItem);
    Ответ написан
  • Как навесить обработчик на коллекцию элементов?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Проблема в том, что вы пытаетесь навесить обработчик на коллекцию элементов, которая возвращается функцией querySelectorAll. Такой возможности нет, нужно обходить эту коллекцию и навешивать обработчик на каждый элемент отдельно:
    var buttonItems = document.querySelectorAll('.button-item'),
        index, button;
    
    for (index = 0; index < buttonItems.length; index++) {
        button = buttonItems[index];
        button.addEventListener('click', function (event) {
            console.log('click');
            event.preventDefault();
        });
    }

    Пример

    Обратите внимание, что такой способ установки обработчика в цикле не очень хорош - при каждой итерации создается новая функция-обработчик, что засоряет память и этот обработчик невозможно потом удалить, поскольку создаваемая функция безымянна и ссылка на нее нигде не сохраняется.
    Ну в принципе такой код не очень опрятный.

    Лучше делать так:
    var buttons = document.querySelectorAll('.button-item'),
        index, button;
    
    for (index = 0; index < buttons.length; index++) {
        button = buttons[index];
        button.addEventListener('click', clickHandler);
        button.addEventListener('dblclick', doubleClickHandler);
    }
    
    function clickHandler(event) {
        console.log('click', this.innerText);
        event.preventDefault();
    }
    
    function doubleClickHandler(event) {
        console.log('doubleclick', this.innerText);
        this.removeEventListener('click', clickHandler);    
        this.removeEventListener('dblclick', doubleClickHandler);
    }
    Ответ написан
  • Как сделать так, чтобы функция выполнялась только после того, как другая завершится?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Дисклеймер
    Кому не нравится название "обещания", мысленно заменяйте его на то, которое считаете подходящим. Я выбрал именно его, чтобы концепция, лежащая в их основе, была интуитивно понятна.

    Если функция асинхронная, то лучше всего использовать обещания, что вы и попытались сделать (интерактивный пример).
    one().done(two);
    
    function one() {
        var dfd = new $.Deferred();
    
        // Запускаем асинхронную задачу. Например, ajax-запрос.
        setTimeout(function () {
            var foo = 'bar';
    
            // "Выполняем обещание", передавая в него какую-то информацию.
            // Передавать аргументы, разумеется, не обязательно.
            dfd.resolve(foo);
        }, 2000);
    
        // Возвращаем из функции обещание, на которое могут подписаться другие функции.
        // Обратите внимание, этот код выполнится до того, как завершится асинхронная задача.
        return dfd.promise();
    }
    
    function two(foo) {
        // Обрабатываем данные, полученные внутри асинхронной функции one.
        console.log('two', foo);
    }

    Для трех функций расклад немного сложнее, но принцип такой же.
    Есть и более элегантный способ запуска цепочки из трех функций:
    код
    one().then(two, onOneError).then(three, onTwoError);
    
    function one() {
        var dfd = new $.Deferred();
    
        setTimeout(function () {
            console.log('one');
            
            if (Math.round(Math.random() * 10) >= 5)
            {
                dfd.resolve();
            }
            else
            {
                dfd.reject();
            }
        }, 1000);
    
        return dfd.promise();
    }
    
    function two() {
        var dfd = new $.Deferred();
    
        setTimeout(function () {
            console.log('two');
            
            if (Math.round(Math.random() * 10) >= 5)
            {
                dfd.resolve();
            }
            else
            {
                dfd.reject();
            }
        }, 1000);
    
        return dfd.promise();
    }
    
    function three() {
        setTimeout(function () {
            console.log('three');
        }, 1000);
    }
    
    function onTwoError() {
        console.log('twoError', arguments);
    }
    
    function onOneError() {
        console.log('oneError', arguments);
    }

    Обратите внимание, что в этом примере показано, что обещания можно не только выполнять, но и отказываться от них и такие ситуации нужно обрабатывать отдельно.


    Обещания - самый современный и удобный вариант, с ними код становится чище и понятнее. Единственная проблема, связанная с ними - это необходимость использовать сторонние библиотеки или полифилы, потому что обещания пока поддерживаются далеко не во всех браузерах.

    Другой вариант - передавать callback, но это прямой путь в callback hell. Для запуска трех и более функций подряд я его не рекомендую - смотрите сами, на что становится похож код:
    one(function () {
        two(three)
    });
    
    function one(callback) {
        console.log('one');
        setTimeout(callback, 1000);
    }
    
    function two(callback) {
        console.log('two');
        setTimeout(callback, 1000);
    }
    
    function three() {
        console.log('three');
    }


    Есть еще один (очень, очень, очень плохой) вариант, основанный на таймерах и внешних флагах. Никогда так не делайте (код для системы из трех функций еще хуже).

    Если внутри функций ничего асинхронного нет, то можно просто вызвать их друг за другом - следующая и так запустится после предыдущей (пример).
    Ответ написан
    5 комментариев
  • JS API и REST API - это разные понятия?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Вы путаете теплое с мягким.
    REST - это методология, работа с таким апи может быть реализована на любых технологиях (включая js), реализация на стороне сервера - тоже.
    JS API - это уже конкретная реализация клиентской библиотеки. На сервере, с которым работает эта библиотека, при этом может быть REST апи.
    Ответ написан
    2 комментария
  • Как правильно отлаживать JS?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега JavaScript
    Как узнать, в каком месте вылезла ошибка?
    console.error выводит полный стек вызовов со ссылками на конкретные строки конкретных скриптов.
    Еще очень удобно включить режим "Pause on exceptions", в нем при выпадении ошибки автоматически включается дебагер. Но для этого Developer Tools должны быть открыты (хотя, если вы веб-разработчик, то они в принципе никогда не закрываются).
    Ну а все остальные проблемы легко решаются при помощи дебагера. Там и значения переменных и стек вызовов и логику можно пошагово посмотреть.
    Ответ написан
    2 комментария