Skip to content

Instantly share code, notes, and snippets.

@if0rest
Last active December 20, 2021 13:16
Show Gist options
  • Save if0rest/aa16e3aa3854fc4e9b2d513912a4e01d to your computer and use it in GitHub Desktop.
Save if0rest/aa16e3aa3854fc4e9b2d513912a4e01d to your computer and use it in GitHub Desktop.

СТРУКТУРА ПРОЕКТА

  • myprojet/
    • app/
      • css/
      • fonts/
      • img/
      • js/
      • sass/
      • index.html
    • dist/
    • node_modules/
    • gulpfile.js
    • package.json

JS

JavaScript - это язык программирования в веб, созданный для добавления поведения и интерактивности страницам. Весь JS работает таким образом: мы в браузере цепляемся к событиям, которые есть у объектов, и при их наступлении меняем свойства объекта с помощью методов.

Как работает браузер?

  • Браузер загружает html-документ с сервера, парсит его (анализирует), преобразуя html в DOM-дерево (объектное представление html).
  • Потом он отрисовывает (рендерит) дерево на экране в соответствии с CSS.
  • Затем позволяет пользователю что-то делать.

Так вот, JS исп., чтобы изменить этот DOM. Если вы меняете дерево налету без перезагрузки страницы, браузер перерисовывает ее. Кроме того, браузер перехватывает действия пользователя, генерирует события и вызывает обработчики, привязанные к конкретному элементу дерева (напр., submit в форме).

JS оживляет страничку. Когда браузер получил HTML-документ, он анализирует его и строит на основе вложенных элементов (тегов) DOM-дерево. У каждого элемента есть обработчики событий.

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


Чтобы хранить информацию, используются ПЕРЕМЕННЫЕ. Переменная состоит из имени и выделенной области памяти. После объявления, можно записать в переменную данные, кот. будут сохранены и доступны при обращении по имени.

Функции, объявленные как Function Declaration function(), создаются интерпретатором до выполнения кода, поэтому их можно вызвать до объявления. А если бы это было объявление Function Expression var = function, то такой вызов бы не сработал.

Это из-за того, что JavaScript перед запуском кода ищет в нём Function Declaration (их легко найти: они не являются частью выражений и начинаются со слова function) и обрабатывает их. А Function Expression создаются в процессе выполнении выражения, в котором созданы, в данном случае — функция будет создана при операции присваивания sayHi = function...{}

Если переменная содержит функцию, то переменная - тоже функция.


NaN возвращается, когда мы делаем что-то, чего в обычной математике делать нельзя.


ВСПЛЫТИЕ

  • event.stopPropagation()

Всплытие идёт прямо наверх. Обычно событие будет всплывать наверх до элемента , затем до document, а иногда и до window, вызывая все обработчики на своем пути. Но любой промежуточный обработчик может решить, что событие полностью обработано, и остановить всплытие. Для остановки всплытия нужно вызвать метод event.stopPropagation().

  • event.stopImmediatePropagation()

Если у элемента есть несколько обработчиков на одно событие, то даже при прекращении всплытия все они будут выполнены. Т.е., stopPropagation препятствует продвижению события дальше, но на текущем элементе все обработчики отработают. Для того, чтобы полностью остановить обработку, современные браузеры поддерживают метод event.stopImmediatePropagation(). Он не только предотвращает всплытие, но и останавливает обработку событий на текущем элементе.

АЛГОРИТМ:

  1. При наступлении события – элемент, на котором оно произошло, помечается как «целевой» ( event.target ).
  2. Далее событие сначала двигается вниз от корня документа к event.target, по пути вызывая обработчики, поставленные через addEventListener(..., true) .
  3. Далее событие двигается от event.target вверх к корню документа, по пути вызывая обработчики, поставленные через on* и addEventListener(..., false). Каждый обработчик имеет доступ к СВОЙСТВАМ СОБЫТИЯ:
  4. event.target – самый глубокий элемент, на котором произошло событие.
  5. event.currentTarget (= this ) – элемент, на котором в данный момент сработал обработчик (до которого «доплыло» событие).
  6. event.eventPhase – на какой фазе он сработал (погружение =1, всплытие = 3).

Любой обработчик может ОСТАНОВИТЬ СОБЫТИЕ вызовом event.stopPropagation(), но делать это не рекомендуется, так как в дальнейшем это событие может понадобиться, иногда для самых неожиданных вещей. В современной разработке стадия погружения используется очень редко. Метод event.stopImmediatePropagation() не только предотвращает всплытие, но и останавливает обработку событий на текущем элементе.


МОДУЛИ в JS

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

Первым делом нужно создать пространство имен: оно защитит var вашего модуля от глобальных var (это важно, если они одноименные). Мы знаем, что 'функцию можно обернуть в скобки, чтобы защитить от глобальной области видимости'. Но что, если мы хотим иметь доступ к этим var из приложения? Для этого нужно создать var и присвоить ее самоосуществляющейся функции, тогда мы получим доступ к содержимому.

   var ray = (function() {
      var private;   // эти var не будут доступны вне функции
   })();

Мы создали пространство имен для var ray, так что в эту функцию доступ у нас есть. Если нужно вытащить или осуществить что-то из функции вне основного приложения, исп. утверждение возврата return. кот. поможет связаться с остальной частью приложения. Так мы сможем сделать видимым то, о чем наше app должно знать. Хотя вместо возврата устверждения мы можем вернуть объект, а через объект создать var и функции.

Исп. return, чтобы вернуть объект. В этом объекте добавим элемент speak().

   var ray = (function() {
      return {
         speak: function() {
            console.log('hello');
         }
      };
   })();

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

   <script>
      ray.speak();   // в консоли появится hello
   </script>

Так мы создали модуль и можем выполнить его из основного приложения, пространство имен при это останется защищенным. Впрочем, главное, на что стоит обратить внимание, мы добавили пространство имен, чтобы защитить содержимое модуля от глобальных var. Плюс мы исп. return для выполнения тех функций, кот. должно выполнять наше app.

var module = (function() {
   return {
      method: function() {
         console.log('I am Method');
      },
      prop: "I am Prop";
   };
})();

Проблема с появлением/исчезанием бургера

$(function() {

   $('.menuToggle').on('click', function() {

      $('.menu').slideToggle(300, function() {

         if ($(this).css('display') === 'none') {
            $(this).removeAttr('style');
         }
      })
   })
});

// плохо
   $('#id').data(key,value);
// лучше (быстрее)
   $.data('#id',key,value);

КОНФЛИКТЫ РАЗНЫХ ВЕРСИЙ JQUERY

#1

   // подключаем последнюю версию jQuery
   // подключаем jQuery 1.6
   var $jq16 = jQuery.noConflict(true);  // сохраняем ссылку
   // подключаем jQuery 1.4
   var $jq14 = jQuery.noConflict(true);  // сохраняем ссылку

   ;(function($) {
      // старый плагин, который использует jQuery 1.4
   }($jq14));

   ;(function($) {
      // чуть новее, использует jQuery 1.6
   }($jq16));

   ;(function($) {
      // используем последнюю версию jQuery
   }(jQuery));

#2

Q: Можете развернуто ответить как это поможет устранить конфликт между двумя версиями jQuery при использовании произвольных скриптов без необходимости их переработки?

A: Наименее затратным в плане модификаций я вижу примерно такой способ:

<script src="jquery.1.7.2.js"></script>
<script>
   var jquery_1_7_2 = jQuery;
</script>
<script src="jquery.1.3.2.js"></script>

<script>
// все скрипты лайтбокса надо обернуть в такой код
   (function(){
      var $ = jQuery = jquery_1_7_2;
         // тут код лайтбокса, что был в файле до нашего вмешательства.
   })();
</script>

Тогда старые скрипты сохранят работоспособность, и лайтбокс будет работать с версией 1.7.2

#3 Подключаете нужную библиотеку jquery и сразу за ней свой скрипт script.js:

   var MyApp.jQuery = jQuery.noConflict();
   // работаете в своем коде с jquery через переменную MyApp.jQuery
   MyApp.jQuery(function($) {
      $(...)...
   });

Универсальный jQuery-скрипт для блоков с вкладками

// v1

(function($) {
$(function() {

   $('ul.tabs').each(function() {
      $(this).find('li').each(function(i) {
         $(this).click(function(){
            $(this).addClass('active').siblings().removeClass('active')
               .parents('div.tabs').find('div.tabs__content').eq(i).fadeIn(150).siblings('div.tabs__content').hide();
         });
      });
   });

})
})(jQuery)


// v2 (need jQuery > 1.7)

(function($) {
$(function() {

   $('ul.tabs__caption').on('click', 'li:not(.active)', function() {
      $(this).addClass('active').siblings().removeClass('active')
         .closest('div.tabs').find('div.tabs__content').removeClass('active').eq($(this).index()).addClass('active');
   })

})
})(jQuery)

Общие выводы:

  • jQuery - популярен из-за своей простоты и расширений
  • Node - единственная реализация веб-сервера для JavaScript
  • Angular-для «шаблонных» сайтов и быстрых решений
  • Backbone - для сложных и гибких решений
  • Ember-для нетривиальных сайтов и настольных онлайн-игр
  • React - для сайтов, которые много и часто работают с DOM

Замыкание = функция + ее лексическое окружение на момент создания.

function hideButNotNow(id, timeout) {
   var elem = document.getElementByld(id);
   var h = function() {
      elem.style['display'] = 'none';
   };

   setTimeout(h, timeout);
}

hideButNotNow('mybtn', 1000);
hideButNotNow('mybtn2', 2000);
hideButNotNow('mybtn3', 3000);

API БРАУЗЕРА

  • window — текущее окно (глобальный объект).
  • document — представляет текущий документ, используется для работы со структурой HTML.
  • location — текущий URL страницы, позволяет работать с отдельными частями URL, изменение location - переход по URL.
  • history — объект для работы с историей страниц, позволяет переходить назад и вперед.
  • navigator — содержит информацию о браузере и операционной системе.
  • cookie — получение и установка куки screen — информация о размерах экрана.

DOM1

  • Node — базовый класс: type, appendChild, removeChild, insertBefore, insertAfter, parentNode, firstChild, childNodes, nextSibling, previousSibling.
  • Document — главный, корневой Node: createElement, createTextNode, createAttribute, getElementsByTagName, getElementByld(), getElementsByName().
  • Element — Node, являющийся тэгом: getAttribute, setAttribute, removeAttribute, getElementsByTagName.
  • Attr — Node, являющийся аттрибутом: name, value.
  • Text — Node, являющиеся фрагментом текста.

ПРЕОБРАЗОВАНИЕ ТИПОВ

console.log(5 + "5");               // 55
console.log(typeof(5 + "5"));       // string
console.log("5" * "4");             // 20
console.log(typeof("5" * "4"));     // number
console.log("5" * "hi");            // NaN
console.log(typeof("5" * "hi"));    // number

console.log("5" == 5);              // true
console.log("0" == false);          // true
console.log(0 == false);            // true
console.log("5"== true);            // false
console.log("" == false);           // true
console.log(null == false);         // false
console.log(null == true);          // false
console.log(undefined == false);    // false
console.log(undefined == true);     // false
console.log(undefined == null);     // true
console.log(Number("555"));             // 555
console.log(typeof(Number("555")));     // number
console.log(String(4433));              // 4433
console.log(typeof(String(4433)));      // string
console.log(Boolean(1));                // true
console.log(typeof(Boolean(1)));        // boolean

console.log(!!5);                       // true
console.log(!!0);                       // false
console.log(typeof(345 + ""));          // string
console.log(typeof +"454");             // number

var number = 22;
console.log(typeof number.toString());  // string
number 45;
console.log(number.toString(3));        // 1200
number 5;
console.log(number.toString(2));        // 101

console.log(typeof false.toString());   // string
console.log(typeof String(Infinity));  // string
console.log(typeof String(NaN));       // string
console.log(+"");                      // 0

console.log(!!"");               // false
console.log(!!NaN);              // false
console.log(!!0);                // false
console.log(!!null);             // false
console.log(!!undefined);        // false

console.log(!!"Hi");             // true
console.log(+"       4 g");      // NaN
console.log(parseInt("4 px"));   // 4

console.log(+true);              // 1
console.log(+false);             // 0

var n = 5;
console.log(n.value);            // undefined

var counter = (function() {
   var count = 0;

   return function() {
      return count++;
   }
}());

console.log(counter());    // 0
console.log(counter());    // 1
console.log(counter());    // 2
console.log(counter());    // 3
console.log(counter());    // 4
console.log(counter());    // 5
console.log(counter());    // 6
console.log(counter());    // 7

function Rabbit() {};               // всего лишь конструктор
Rabbit.prototype = { eats: true };  // переопределяем прототип

var rabbit = new Rabbit();          // создаем экземпляр.
alert(rabbit.eats);
// __proto___ ссылается на прототип конструктора, а значит, ненайденное свойство будет искаться в __proto__.
// rabbit.__proto__ = Rabbit.prototype = { eats: true }

// хуже
$('.my-select:selected');

// лучше
$('.my-select').filter(':selected');

Прерывание событий jQuery

$('.b-link').on('click', function (event) {
   return false;              // preventDefault + stopPropagation
});

$('.b-form').on('submit', function (event) {
   return isValid(this);      // this -> .b-form
});

$('.b-link').on('click', function (event) {
   event.stopPropagation();
   event.preventDefault();
});

function makeCaching(f) {
   var cache = {};

   return function(x) {
      // проверка неоч
      if (!cache[x])
      // проверка вери найс
      if (!x in cache)
         cache[x] = f.call(this, x);

      return cache[x];
   }
}

Задача: Сумма произвольного количества скобок

function sum(a) {
   var currentSum = a;

   function f(b) {
      currentSum += b;
      return f;
   }

   f.toString = function() {
      return currentSum;
   };

   return f;
}

alert( sum(1)(2) );                // 1 + 2 = 3
alert( sum(1)(2)(3) );             // 1 + 2 + 3 = 6
alert( sum(5)(-1)(2) );            // 6
alert( sum(6)(-1)(-2)(-3) );       // 0
alert( sum(0)(l)(2)(3)(4)(5) );    // 15

Добавить get/set-свойства

function User(fullName) {

   this.fullName = fullName;

   Object.defineProperties(this, {
      firstName: {
         get: () => this.fullName.split(' ')[0],
         set: (value) => this.fullName = value + ' ' + this.lastName,
      },
      lastName: {
         get: () => this.fullName.split(' ')[1],
         set: (value) => this.fullName = this.firstName + ' ' + value,
      },
   });
}

var vasya = new User("Василий Попкин");
alert( vasya.firstName );    // Василий
alert( vasya.lastName );     // Попкин

vasya.lastName = 'Сидоров';
alert( vasya.fullName );     // Василий Сидоров
alert( vasya.firstName );    // Василий
alert( vasya.lastName );     // Сидоров

Базовый фильтр :not(filter | selector)

:not(filter | selector) - выбор всех элементов кроме тех, которые попадают под выборку, указанную в круглых скобках.

$("p:not(:eq(3))").css("color", "red");
  1. Параграф
  2. Параграф
  3. Параграф // элемент с индексом 3; не перекрасится
  4. Параграф
  5. Параграф
  6. Параграф
  7. Параграф

Как использовать DOM

table     = document.getElementByld('table');
tbody     = table.getElementsByTagName('tbody')[0];
lastRow   = tbody.lastChild;
clonedRow = lastRow.cloneNode();
tbody.appendChild(clonedRow);
tdList    = clonedRow.getElementsByTagName('td');

for (var i = 0; i < tdList.length; i++) {
   children = tdList[i].childNodes;

   for (var j = 0; j < children.length; j++) {
      tdList[i].removeChild(children[j]);
   }
}

Сортировка <option>

<select>
   <option>-- Выберите отделение --</option>
   <option>Диетология</option>
   <option>Кардиология</option>
   <option>Гастроэнтерология</option>
   <option>Мануальная терапия</option>
   <option>Неврология</option>
   <option>Андрология</option>
</select>

<script>
$(document).ready(function () {
   var $select = $('select');
   var $opts = $select.find('option').sort(function (a, b) {
      a = a.innerHTML, b = b.innerHTML;
      if (a && b) return a.toUpperCase().localeCompare(b.toUpperCase());
      return 0;
   });
   $select.prepend($opts.detach());
});
</script>

Scroll-to-Top button with jQuery

jQuery(document).ready(function($) {
    $("#scrollup").click(function() {
            $("html, body").animate({
                scrollTop: 0
            }, 1e3)
        });
    $(window).scroll(function() {
        200 < $(this).scrollTop() ? $("#scrollup").fadeIn() : $("#scrollup").fadeOut()
    });
})

// Можно задать свойство относительно его текущего значения,
// используя операции += или -= как часть настроек анимации.

$(this).animate ({left : '+=50px'}, 1000);

Предварительная загрузка изображения

Предварительная загрузка изображения означает приказ браузеру загрузить картинку до того, как вы планируете ее отобразить. Когда изображение загружено, оно сохраняется в кэше браузера, чтобы все последующие запросы обслуживались с жесткого диска посетителя, а не загружались с сервера заново.

var preloadimages = [
   'images/roll.png',
   'images/flower.png',
   'images/cat.jpg'
];
for (var i=0; i < preloadimages.length; i++) {
   new Image().src = preloadimages[i];
   // jQuery-аналог
   $('<img>').attr('src', preloadimages[i]);
}

ДЕЛЕГИРОВАНИЕ СОБЫТИЙ С ПОМОШЬЮ .on()

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

У метода присоединения обработчиков событий к элементам есть 1 проблема: он работает, только если элемент уже присутствует на странице. Если вы динамически добавляет HTML-код, то к новым элементам обработчики присоединены не будут.

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

  $('ul').on('click', 'li', function() {
      $(this).css('text-decoration': 'line-through');
  });

В приведенном коде объект $(this) относится к элементу ul, по которому щелкает посетитель. Тем не менее при использовании делегирования первоначальный селектор больше не является элементом, с которым взаимодействует посетитель — он становится контейнером для элемента, по которому щелкает посетитель.

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

В некоторых случаях вы можете применять делегирование событий просто для повышения эффективности кода. Если добавляете большое количество элементов к одному и тому же обработчику событий, например, сотни ячеек таблицы, то часто лучше бывает делегировать событие элементу table следующим образом:

   $('table').on('click', 'td', () => {
      // код помещается здесь
   });

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

jQuery Accordeon + Slick [Fix]

$('.accordeon__plus, .accordeon__title').one('.accordeon__slider .gal_wrapper').slick('unslick').slick('reinit');

if ($body.is(':visible')) {
    $('.accordeon__slider').slick('setPosition', 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment