jQuery – Stepan Suvorov Blog https://stepansuvorov.com/blog Release 2.0 Fri, 01 Sep 2023 15:40:58 +0000 en-US hourly 1 https://wordpress.org/?v=6.3.1 Пишем jQuery c нуля. Часть3 – DOMContentLoaded shortcut https://stepansuvorov.com/blog/2015/05/%d0%bf%d0%b8%d1%88%d0%b5%d0%bc-jquery-c-%d0%bd%d1%83%d0%bb%d1%8f-%d1%87%d0%b0%d1%81%d1%82%d1%8c3-domcontentloaded-shortcut/ https://stepansuvorov.com/blog/2015/05/%d0%bf%d0%b8%d1%88%d0%b5%d0%bc-jquery-c-%d0%bd%d1%83%d0%bb%d1%8f-%d1%87%d0%b0%d1%81%d1%82%d1%8c3-domcontentloaded-shortcut/#comments Mon, 11 May 2015 05:44:06 +0000 http://stepansuvorov.com/blog/?p=2472 Continue reading ]]> В продолжение серии постов “Пишем jQuery c нуля”. Решил осветить тему события загрузки дома(DOMContentLoaded) и регистрацию колбэков по этому событию через jQuery.

Как мы знаем: в jQuery есть вспомогательный метод $.ready(), который принимает параметром функцию и вызывает ее по событию DOMContentLoaded. Попробуем воссоздать у себя в djQuery такой же метод.

Должно быт как-то так:

[javascript]
var callbacks = [];
djQuery.ready = function(callback) {
callbacks.push(callback);
}
[/javascript]

Теперь сделаем, чтобы колбэки выполнились по событию загрузки DOM:

[javascript]
document.addEventListener( "DOMContentLoaded", function() {
callbacks.forEach(function(callback) {
callback.apply();
});
});
[/javascript]

Вынесем функцию отдельно(как это сделано в jQuery) и назовем ее completed:

[javascript]
function completed() {
callbacks.forEach(function(callback) {
callback.apply();
});
}
document.addEventListener( "DOMContentLoaded", completed );
[/javascript]

И еще хорошо бы отписаться от события, когда мы его получим:

[javascript]
function completed() {
document.removeEventListener( "DOMContentLoaded", completed);
callbacks.forEach(function(callback) {
callback.apply();
});
}
document.addEventListener( "DOMContentLoaded", completed );
[/javascript]

Для старых браузеров jQuery проделывает еще 2 вещи:

Ставит 3тий параметр false (useCapture – опция, которая и так выключена для новых версий браузеров):

[javascript]
document.addEventListener( "DOMContentLoaded", completed, true);
[/javascript]

и дополнительно слушает еще одно событие:

[javascript]
window.addEventListener( "load", completed, false );
[/javascript]

Мы этого всего для нашего djQuery делать не будем, так как

DOMContentLoaded support

Плюс нам еще осталось добавить короткую форму записи для $.ready(callback), которая представляет из себя:

[javascript]
$(callback);
[/javascript]

То есть, если мы передадим в jQuery функции как параметр, она будет записана в  $.ready колбэки и выполнится по событию загрузки DOM:

[javascript]
if(typeof selector === ‘function’){
djQuery.ready(selector);
return this;
}
[/javascript]

Теперь для проверки отдельно от нашей библиотеки пишем:

[javascript]
djQuery(function(){
console.log(‘DOMContentLoaded’);
});
[/javascript]

или просто:

[javascript]
$(function(){
console.log(‘DOMContentLoaded’);
});
[/javascript]

Код можно взять в теге step-3.

]]>
https://stepansuvorov.com/blog/2015/05/%d0%bf%d0%b8%d1%88%d0%b5%d0%bc-jquery-c-%d0%bd%d1%83%d0%bb%d1%8f-%d1%87%d0%b0%d1%81%d1%82%d1%8c3-domcontentloaded-shortcut/feed/ 7
Пишем jQuery c нуля. Часть2 – Поисковый движок и вывод результатов https://stepansuvorov.com/blog/2015/05/jquery-from-sketch-sizzle/ https://stepansuvorov.com/blog/2015/05/jquery-from-sketch-sizzle/#respond Mon, 04 May 2015 04:53:52 +0000 http://stepansuvorov.com/blog/?p=2469 Continue reading ]]> В продолжение рубрики “Пишем jQuery c нуля” хотел бы рассказать о внутреннем поисковом движке, той ключевой функциональности, которая и дала название “jQuery” (Javascript query). Плюс рассмотрим момент инициализации/создания jQuery объекта.

Все разработчики, которые хоть раз использовали jQuery, знают, что если написать:

[javascript]
$(‘div.myclass’)
[/javascript]

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

Поисковый движок получил имя Sizzle и в последствии был выделен в отдельную библиотеку, которую включает в себя jQuery.

С появлением в браузерах методов для поиска элементов по селекторам – querySelector, querySelectorAll острая необходимость в Sizzle пропадает. Мы его можем использовать только для старых браузеров и использовании расширенных поисковых фильтров.

Итого как себя ведет jQuery получив строку-селектор параметром:

  1. анализ регулярным выражением
  2. если это id – то получаем элемент с помощь document.getElementById
  3. если есть возможность, используем querySelectorAll
  4. если нет возможности использовать querySelectorAll – используем Sizzle

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

Выделю только ключевые моменты:

  •  если Sizzle обнаруживает #id селектор внутри строки, то он игнорирует остальное, то есть:

[javascript]
$(‘div.myclass#myelem’)
[/javascript]

и

[javascript]
$(‘#myelem’)
[/javascript]

будут восприняты одинаково

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

Так ну а теперь немного кода, чтобы дополнить наш djQuery проект.

Как мы и договорились поддерживать старые браузеры не будем, поэтому ограничимся использованием querySelector:

[javascript]
var result = document.querySelectorAll(selector);
[/javascript]

Но просто результат мы не можем вернуть, так как помним, что jQuery возвращает свой экземпляр, поэтому:

[javascript]
return this;
[/javascript]

ну и предварительно наполним этот объект результатами:

[javascript]
for (var i = 0; i < results.length; i++) {
this[i] = results[i];
}
this.length = i;
[/javascript]

все бы хорошо, но правильно работать будет только, если мы сделаем new, то есть вызов:

[javascript]
new djQuery(‘div’)
[/javascript]

это не совсем то, чего мы хотели. Как создать контекст уже внутри конструктора?

Хочется сделать как-то так:

[javascript]
var djQuery = function(selector, context) {
return new djQuery(selector, context);
};
[/javascript]

Только, понятное дело, внутренняя функция не может дублировать внешнюю. Поэтому сделаем метод init, в который перенесем внутренности djQuery инициализации:

[javascript]
var djQuery = function(selector, context) {
return new init(selector, context)
};

init = function( selector ) {
var results = document.querySelectorAll(selector);
for (var i = 0; i < results.length; i++) {
this[i] = results[i];
}

this.length = i;
};
[/javascript]

А для того, чтобы все методы прототипа djQuery были доступны из объектов созданных с помощью init конструктора свяжем их прототипы:

[javascript]
init.prototype = djQuery.prototype;
[/javascript]

Ну вот теперь можем выполнить:

[javascript]
djQuery(‘div’)
[/javascript]

И получить ожидаемый список объектов.

Но если мы сравним в консоли результаты функций (нашей и jQuery), то можем заметить следующее:

jQuery vs djQuery

Почему-то jQuery выводит результаты в виде массива, а наша djQuery возвращает объект. Оказывается, чтобы объект воспринимался как массив в нем должны присутствовать следующие метод splice. Ну что ж, добавим их в прототип вместе с push и sort:

[javascript]
djQuery.prototype = {
length: 0,
push: [].push,
sort: [].sort,
splice: [].splice
};
[/javascript]

И… Ура! мы добились того, чего хотели:

jQuery vs djQuery array view

Еще добавим небольшую проверочку для случая, когда селектор не был передан:

[javascript]
if(!selector){
return this;
}
[/javascript]

Текущая версия кода в теге step-2.

]]>
https://stepansuvorov.com/blog/2015/05/jquery-from-sketch-sizzle/feed/ 0
Пишем jQuery c нуля. Часть1 https://stepansuvorov.com/blog/2015/03/jquery-from-sketch/ https://stepansuvorov.com/blog/2015/03/jquery-from-sketch/#comments Tue, 17 Mar 2015 20:38:49 +0000 http://stepansuvorov.com/blog/?p=2465 Continue reading ]]> djQuery

Начинаю новую рубрику статей посвященных внутренностям jQuery. Материал должен стать полезен как начинающим JavaScript/jQuery разработчикам, так и опытным специалистам. Шаг за шагом мы воссоздадим функциональность самой популярной на сегодняшний день библиотеки.

Специально для этого я создал репозиторий на gitHub(с кодовым именем djQuery), где буду выкладывать код рубрики:

[shell]
git clone git@github.com:stevermeister/djQuery.git
cd djQuery
git checkout step-1
[/shell]

Ну что начнем?

– Что представляет из себя jQuery?
– Это функция.
– какие принимает параметры?
– селектор, ну и что-то еще может

Отлично, тогда напишем такой базовый код:

[javascript]
var djQuery = function(selector, content){

};
[/javascript]

Пока вроде все понятно.

Еще сразу, для красоты модульной, завернем все в самовыполняющуюся функцию:

[javascript]
(function(w){
var djQuery = function(selector, content){

};

w.$ = w.djQuery = djQuery;
})(window);
[/javascript]

и внутрь передали объект window и импортировали в него ссылку на нашу функцию(плюс привычный $ – алиас).

Думаю для первой части хватит. Код можно взять по тегу step-1.

git checkout step-1

Больше кода обещаю во второй части ;)

]]>
https://stepansuvorov.com/blog/2015/03/jquery-from-sketch/feed/ 4
Nowadays: From jQuery to JavaScript https://stepansuvorov.com/blog/2014/11/nowadays-from-jquery-to-javascript/ https://stepansuvorov.com/blog/2014/11/nowadays-from-jquery-to-javascript/#respond Fri, 07 Nov 2014 20:25:51 +0000 http://stepansuvorov.com/blog/?p=2077

If you are jQuery-coder but would like to become real JavaScript Engineer, Youmightnotneedjquery is good resource for you.

]]>
https://stepansuvorov.com/blog/2014/11/nowadays-from-jquery-to-javascript/feed/ 0
Как правильно готовить Angular https://stepansuvorov.com/blog/2014/07/%d0%ba%d0%b0%d0%ba-%d0%bf%d1%80%d0%b0%d0%b2%d0%b8%d0%bb%d1%8c%d0%bd%d0%be-%d0%b3%d0%be%d1%82%d0%be%d0%b2%d0%b8%d1%82%d1%8c-angular/ https://stepansuvorov.com/blog/2014/07/%d0%ba%d0%b0%d0%ba-%d0%bf%d1%80%d0%b0%d0%b2%d0%b8%d0%bb%d1%8c%d0%bd%d0%be-%d0%b3%d0%be%d1%82%d0%be%d0%b2%d0%b8%d1%82%d1%8c-angular/#comments Mon, 07 Jul 2014 11:17:56 +0000 http://stepansuvorov.com/blog/?p=1771 Continue reading ]]> Пост составлен на основе развернутого ответа John David Miller о том, как человеку, который писал сайты с jQuery, переключится на AngularJS – мышление.

1. Проектируйте страницу в момент написания HTML

В jQuery-ориентированном подходе вы сначала верстаете страницу, а потом – делаете ее “динамической”.

В AngularJS вы должны начинать с архитектуры в самом начале. Вместо того, чтобы думать “У меня есть этот кусок DOM и я хочу заставить его делать что-то такое …”, сразу думать о том, что хотите достичь в целом(от приложения), и только затем переходить к структуре, и в самую последнюю очередь – к дизайну конкретного отображения.

2. Не пытайтесь “разогнать” jQuery с помощью AngularJS

Проще говоря: не начинайте с мысли “у меня есть jQuery, оно делает делает X, Y и Z, и я просто добавлю AngularJS для создания моделей и контроллеров”. Это очень заманчиво, особенно тогда, когда вы только начали работать с AngularJS и еще не понимаете всех концептов. Именно поэтому я всегда рекомендую начинающим AngularJS разработчикам не использовать jQuery вообще. До тек пор, пока они не почувствуют как делать “Angular-way“.

Я видел много разработчиков создающих эти запутанные решения с jQuery плагинами на 150-200 строчек кода, которые они пытаются прикрутить к AngularJS с помощью набора колбеков совместно с использованием $apply, что крайне запутано и сбивает с толку, но они таки заставляют “Это” работать. Проблема состоит в том, что в большинстве случаев jQuery плагин может быть переписан в несколько строчек AngularJS, где все бы стало на свои места и было просто и понятно.

Суть заключается в следующем: когда вы стараетесь думать “Angular-way“, даже если вы не можете сразу решить задачу – спросите Angular-сообщество, если и после этого решение не было найдено – используйте jQuery. Но не позволяйте jQuery стать костылем для всех решений, иначе вы никогда не освоите AngularJS.

3. Всегда думаете в терминах архитектуры

Single page applications – это приложение, но не в коем случае не набор веб-страниц. Поэтому мы должны думать не только как разработчики клиентской части, но так же как сервер-сайд разработчики(в плане взаимодействия данных). Мы должны думать о том, как разбить наше приложение на индивидуальные, расширяемые компоненты, которые легко будет потом покрыть тестами.

И как это сделать? Как думать Angular-way? Далее представлены основные принципы, контрастирующие с jQuery:

Место действия View

В jQuery мы программно изменяем представление(view). Рассмотрим пример выпадающего меню:

[html]
<ul class="main-menu">
<li class="active"><a href="#/home">Home</a></li>
<li><a href="#/menu1">Menu 1</a>
<ul>
<li><a href="#/sm1">Submenu 1</a></li>
<li><a href="#/sm2">Submenu 2</a></li>
<li><a href="#/sm3">Submenu 3</a></li>
</ul>
</li>
<li><a href="#/home">Menu 2</a></li>
</ul>
[/html]

В jQuery мы активируем логику нашего приложения как-то так:

[javascript]$(‘.main-menu’).dropdownMenu();[/javascript]

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

В AngularJS представление определяет логику представления. Наше объявление View будет следующим:

[html]
<ul class="main-menu" dropdown-menu>…</ul>
[/html]

Обе версии(jQuery & AngularJS) делают одно и тоже, но в случае с AngularJS – любой смотря в шаблон может понять суть. Когда новый разработчик изучает проект, он может посмотреть на этот код  и сделать вывод что тут используется директива dropdownMenu. Представление раскажет все, что ожидается в плане функциональности намного яснее, чем какой-то сторонний код, прикрепленный непонятно где.

Разработчики начиная использовать AngularJS часто задают вопрос типа: как я могу выбрать все нужные элементы и добавить к ним директиву. И они застынут от изумления, когда услышат ответ “вам не нужно искать элементы”. Но причина, по которой вы не дожны так делать: это полу-jQuery полу-AngularJS – что к добру не приведет. Проблема тут заключается в том, что разработчик пытается “делать jQuery” в контексте AngularJS. Снаружи директивы вы никогда не изменяете DOM, а директивы применяются во View. Замысел должен быть ясен.

Запомните: не работаете над дизайном, а потом версткой. Вы должны работать над архитектурой, а потом дизайном.

Связывание данных (Data binding)

Это одна из самых крутых фишек AngularJS. Она отбрасывает кучу не нужной работы с DOM. AngularJS автоматически обновит ваше представление и вам не нужно будет этого делать! В jQuery, мы реагируем на события и обновляем контент:

[javascript]
$.ajax({
url: ‘/myEndpoint.json’,
success: function ( data, status ) {
$(‘ul#log’).append(‘<li>Data Received!</li>’);
}
});
[/javascript]

а представление выглядит так:

[html]
<ul id="log" class="messages">
</ul>
[/html]

Как видим, у нас теже проблемы. Но что более важно – мы должны в ручную обновлять DOM. А если мы захотим удалить один элемент из списка – мы тоже должны писать для этого дополнительный код. Как мы можем протестировать логику отдельно от DOM? И что, если я хочу изменить представление?

Немного хардкорно, но в AngularJS мы можем сделать так:

[javascript]
$http( ‘/myEndpoint.json’ ).then( function ( response ) {
$scope.log.push( { msg: ‘Data Received!’ } );
});
[/javascript]

И представление будет выглядеть следующим образом:

[html]
<ul class="messages">
<li class="alert" ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>
[/html]

Но наше представление так же может выглядеть так:

[html]
<div class="messages">
<div class="alert" ng-repeat="entry in log">{{ entry.msg }}</div>
</div>
[/html]

Теперь мы используем бустраповские алерты вместо списка. И мы даже не прикасались к коду контроллера! Но еще важнее, что каким бы способом не обновлялся log – представление тоже обновится. Автоматически!

Связывание данных двух стороннее(two-way). Лог-сообщения могут быть редактируемыми, просто вот так:

[html]
<input type="text" ng-model="entry.msg"/>
[/html]

Разве не повод для радости?

Выделение уровня модели

В jQuery DOM является моделью, но в AngularJS у нас отдельный уровень модели, которым мы можем управлять как захотим, и полностью независимо от представления.

Разделение концептов

Ваше представление (View) определяет поведение, ваша модель представляет данные, ваш уровень сервисов служит для различных задач, которые будут выполнятся не один раз. Вы работаете с DOM и расширяете представление в директивах, и соединяете все вместе с помощью контроллеров.

Внедрение зависимости (Dependency injection)

В разделении концептов нам помогает DI. Если вы пришли с серверных языков (от Java до PHP), вы вероятно уже знакомы с данным термином, но если вы разрабатывали только клиент на jQuery, этот концепт может показаться глупым либо хипстерским. Но это далеко не так. :)

В общем случае DI дает возможность объявлять компоненты очень свободно и из разных других компонент, просто запросите экземпляр в нужном месте и он будет предоставлен. Вам не нужно задумываться о порядке загрузки, или размещении файлов или еще о чем-то таком. Мощь механизма может быть не сразу ощутима, но я приведу лишь один общий пример – тестирование.

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

4. Разработка через тестирование (Test-driven development) – всегда

На самом деле это тоже часть секции 3, но это довольно важно, поэтому выношу в отдельный пункт.

Из множества jQuery плагинов, которые вы видели/использовали, сколько из них идут покрытые тестами? – Не так много. Это потому что jQuery не очень поддается тестированию, а вот AngularJS в тестировании очень хорош.

В jQuery единственный способ протестировать – создавать отдельный от приложения компонент на демо- страничке, на которой будет произведены DOM-манипуляции. То есть, мы должны разрабатывать компонент отдельно, а потом интегрировать его в приложение. Как неудобно!

Но, так как в AngularJS мы используем разделение концептов, то мы можем легко делать TDD! Например: мы хотим простую директиву для индикации роли в меню. Мы можем объявить что мы хотим в нашем представлении:

[html]
<a href="/hello" when-active>Hello</a>
[/html]

Теперь мы можем написать тест:

[javascript]
it( ‘should add "active" when the route changes’, inject(function() {
var elm = $compile( ‘<a href="/hello" when-active>Hello</a>’ )( $scope );

$location.path(‘/not-matching’);
expect( elm.hasClass(‘active’) ).toBeFalsey();

$location.path( ‘/hello’ );
expect( elm.hasClass(‘active’) ).toBeTruthy();
}));
[/javascript]

Запускаем тест, убеждаемся, что он не проходит, и пишем директиву:

[javascript]
.directive( ‘whenActive’, function ( $location ) {
return {
scope: true,
link: function ( scope, element, attrs ) {
scope.$on( ‘$routeChangeSuccess’, function () {
if ( $location.path() == element.attr( ‘href’ ) ) {
element.addClass( ‘active’ );
}
else {
element.removeClass( ‘active’ );
}
});
}
};
});
[/javascript]

Теперь тест проходит и меню рабоает. TDD.

5. Директивы – это не запакованый jQuery код

Вы часто слышите “работа с DOM только в директиве”. Это необходимо. Отнеситесь к этому серьезно!

Но давайте нырнем глубже…

Некоторые директивы просто являются декораторами того, что уже есть в представлении (ngClass) и поэтому иногда работают с DOM напрямую(без нашего кода). Но, если директива типа “виджет” и имеет шаблон, она также должна следовать принципу разделения концептов. То есть, шаблон также должен быть независим от области его применения в link-методе и контроллере.

В AngularJS есть целый набор готовых директив, что серьезно упрощает нашу работу: с ngClass мы можем динамически обновлять классы, ngBind позволяет делать двусторонне связывание данных, ngShow и ngHide показывают и прячут элемент. Другими словами: мы можем делать “конфетку” даже без манипуляций с DOM. Чем меньше работы с DOM – тем проще тестировать директиву, а также изменять в будущем.

Я вижу многие начинающие AngularJS используют директивы как место, куда можно вкинуть jQuery код. Другими словами: они думают “если я не могу работать с DOM в контроллере, то я напишу этот код в директиве”. Это намного лучше, но все еще не angualr-way.

Обратите внимание на систему логирования, которую мы описали в разделе 3. Даже, если мы поместим jQuery код в директиву, мы по прежнему хотим делат это angular-way. Есть много случаев, когда работа с DOM необходима, но этих случаев намного меньше, чем вы думаете! До того как сделать какие-либо операции с DOM, спросите себя нужны ли они вам. Возможно есть лучший способ, который позволит обойтись без них использовав встроенные инструменты фреймворка.

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

[javascript]
.directive( ‘myDirective’, function () {
return {
template: ‘<a class="btn">Toggle me!</a>’,
link: function ( scope, element, attrs ) {
var on = false;
$(element).click( function () {
if ( on ) {
$(element).removeClass( ‘active’ );
} else {
$(element).addClass( ‘active’ );
}

on = !on;
});
}
};
});
[/javascript]

Но что-то с ним не так.

  • jQuery тут совсем не нужно.
  • даже если у нас где-то есть jQuery код на странице, то совсем не обязательно использовать его тут для совместимости.  Мы можем просто использовать angular.element и все зависимости продолжат работать в проекте без jQuery
  • даже если jQuery нам необходим для использования конкретно в этой директиве, то мы опять же всегда можем использовать angular.element, который представляет из себя jqLite, а если jQuery подключена к проекту, то ссылку на jQuery. То есть нет необходимости использовать $ вобще – вместо него лучше использовать angular.element.
  • довольно тесно идет с предыдущим пунктом: element, который получает функция link как параметр уже является экземпляром jqLite и его не нужно дополнительно оборачивать в jQuery.
  • что мы говорили о смешении шаблона и представления с логикой в предыдущей секции?

Эта директива может быть переписана следующим образом:

[javascript]
.directive( ‘myDirective’, function () {
return {
scope: true,
template: ‘<a class="btn" ng-class="{active:on}" ng-click="toggle()">Toggle me!</a>’,
link: function ( scope, element, attrs ) {
scope.on = false;
scope.toggle = function () {
scope.on = !scope.on;
};
}
};
});
[/javascript]

Опять же таки: логика и шаблон идут раздельно, тестирование делать просто, изменнеия вносить просто: как шаблона, так и логики.

Итак, директивы – это не просто коллекции jQuery-подобных функций. Но что же они? Директивы – это расширение HTML. Если HTML не позволяет сделать то, что вам нужно сделать – вы пишете директиву, и используете ее так, как будто это часть HTML.

Или по другому: если AngularJS что-то не делает “из коробки”, то подумайте, как бы вы могли это правильно сделать с ngClick, ngClass и так далее.

Итого

Даже не пытайтесь использовать jQuery. Даже не подключайте ее. И каждый раз, при желании воспользоваться $, думайте как бы вы могли сделать это в рамках AngularJS. Если не знаете – спросите! В 19ти случаях из 20ти лучший способ решения задачи не требует использования jQuery.

]]>
https://stepansuvorov.com/blog/2014/07/%d0%ba%d0%b0%d0%ba-%d0%bf%d1%80%d0%b0%d0%b2%d0%b8%d0%bb%d1%8c%d0%bd%d0%be-%d0%b3%d0%be%d1%82%d0%be%d0%b2%d0%b8%d1%82%d1%8c-angular/feed/ 9
jQuery to String https://stepansuvorov.com/blog/2014/06/jquery-to-string/ https://stepansuvorov.com/blog/2014/06/jquery-to-string/#respond Sat, 28 Jun 2014 08:14:21 +0000 http://stepansuvorov.com/blog/?p=1775 Continue reading ]]> Иногда возникает необходимость получить html-содержимое jQuery-объекта в виде строки. Первое что приходит в голову

[javascript]$element.html();[/javascript]

да, это работает, но мы не получаем html-кода самого контейнера (или корневого элемента). Следующий вариант, который приходит в голову – сделать обертку копии текущего элемента и после уже получить содержимое:

[javascript]$(‘<div>’).append($element.clone()).html();[/javascript]

но как-то это громоздкою.
Погуглив нашел еще интересный вариант, который работает в jQuery начиная с версии 1.6:

[javascript]$element.prop(‘outerHTML’); [/javascript]

Ну и можно еще поиграть с переопределением метода toString (сделано в качестве эксперимента и крайне не рекомендуется делать в реальном проекте):

[javascript]
$.fn.toString = function() {
return $(this).prop(‘outerHTML’);
}
[/javascript]

после чего

[javascript]alert($element) [/javascript]

выведет не просто [Object object], а строку – HTML содержимое объекта.

И код.

]]>
https://stepansuvorov.com/blog/2014/06/jquery-to-string/feed/ 0
Delayed Keypress или создаем свои хуки событий на jQuery https://stepansuvorov.com/blog/2014/06/delayed-keypress-jquery/ https://stepansuvorov.com/blog/2014/06/delayed-keypress-jquery/#comments Sat, 14 Jun 2014 15:50:49 +0000 http://stepansuvorov.com/blog/?p=1683 Continue reading ]]> Иногда возникает необходимость расширить стандартное событие JavaScript/jQuery, добавить определенную специфику, например: мы хотим выводить авто-дополнение для текстового поля при вводе, при этом мы не хотим чтобы запрос отправлялся на сервер при каждом нажатии клавиши(будет очень много ненужных запросов). Зададимся целью посылать запрос через 500 миллисекунд (полсекунды) после того, как пользователь прекратит набирать(либо остановился в ожидании подсказки авто-дополнения).

Если не создавать своего события, а просто обработать keypress, то получим где-то такой код:

[javascript]
$(function() {
var timer;
$("#id").keypress(function() {
timer && clearTimeout(timer);
timer = setTimeout(someMethod, 500);
});
});
[/javascript]

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

jQuery имеет следующий синтаксис для определения своих событий:

[javascript]
jQuery.event.special.myevent = {
delegateType: ‘eventType’,
bindType: ‘eventType’,
setup: function( data, namespaces, eventHandle ) {
// code
},
teardown: function( namespaces ) {
// code
},
add: function( handleObj ) {
// code
},
remove: function( handleObj ) {
// code
},
_default: function( event ) {
// code
},
trigger: function( event: jQuery.Event, data: Object ) {
// code
},
handle: function( event: jQuery.Event, data: Object ){
// code
}
};
[/javascript]

Немного пояснений по свойствам и колбэкам:

  • bindType – тип стандартного события(если такое имеется), к которому хотим привязаться
  • delegateType – какому типу события делегируем последующее выполнение
  • setup – вызовется при первом навешивании обработчика
  • teardown – когда все обработчики сняты
  • add/remove – при каждом добавлении/удалении нового обработчика
  • trigger – вызовется при использовании методов .trigger() и .triggerHandler()
  • _default – определяет нужно ли запускать стандартный обработчик браузера
  • handle – основной метод, который будет вызван при обнаружении события определенного в bindType опции

Если мы хотим просто прокинуть стандартный keypress через свой обработчик, получим:

[javascript]jQuery.event.special.delayedkeypress = {
delegateType: "keypress",
bindType: "keypress",
handle: function (event) {
return event.handleObj.handler.apply(this, arguments);
}
};[/javascript]

а теперь добавим нашу обработку c таймером:

[javascript]
jQuery.event.special.delayedkeypress = {
delegateType: "keypress",
bindType: "keypress",
handle: function (event) {
var self = this;
this.timer && clearTimeout(this.timer);
this.timer = setTimeout(function(){
event.handleObj.handler.apply(self, arguments);
}, 500);
}
};
[/javascript]

это позволит нам превратить первоначальный код в простой отлов события:

[javascript]$("#id").on("delayedkeypress", someMethod);[/javascript]

ну и для полной jquery-ности добавим алиас-метод delayedkeypress для быстрой работы с событием(аналогично click(), focus(), scroll()):

[javascript]
jQuery.fn[ "delayedkeypress" ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( "delayedkeypress", null, data, fn ) :
this.trigger( "delayedkeypress" );
};[/javascript]

получим:

[javascript]$("#id").delayedkeypress(someMethod);[/javascript]

Вот тут можно поиграть с кодом. Более подробную информацию можно найти в исходниках.

]]>
https://stepansuvorov.com/blog/2014/06/delayed-keypress-jquery/feed/ 1
jQuery: $.put and $.delete https://stepansuvorov.com/blog/2014/04/jquery-put-and-delete/ https://stepansuvorov.com/blog/2014/04/jquery-put-and-delete/#comments Tue, 01 Apr 2014 09:52:55 +0000 http://stepansuvorov.com/blog/?p=1644 jQuery Ajax helpers set does not have shortcuts for  PUT and DELETE methods, but nothing prevents us to make them ourselves.

put

$.put = function(url, data, callback, type){

  if ( $.isFunction(data) ){
    type = type || callback,
    callback = data,
    data = {}
  }

  return $.ajax({
    url: url,
    type: 'PUT',
    success: callback,
    data: data,
    contentType: type
  });
}

delete

$.delete = function(url, data, callback, type){

  if ( $.isFunction(data) ){
    type = type || callback,
    callback = data,
    data = {}
  }

  return $.ajax({
    url: url,
    type: 'DELETE',
    success: callback,
    data: data,
    contentType: type
  });
}

And a short form for both is:

jQuery.each( [ "put", "delete" ], function( i, method ) {
  jQuery[ method ] = function( url, data, callback, type ) {
    if ( jQuery.isFunction( data ) ) {
      type = type || callback;
      callback = data;
      data = undefined;
    }

    return jQuery.ajax({
      url: url,
      type: method,
      dataType: type,
      data: data,
      success: callback
    });
  };
});
]]>
https://stepansuvorov.com/blog/2014/04/jquery-put-and-delete/feed/ 4
jQuery Ajax Simple Mock https://stepansuvorov.com/blog/2014/01/jquery-ajax-simple-mock/ https://stepansuvorov.com/blog/2014/01/jquery-ajax-simple-mock/#respond Tue, 21 Jan 2014 15:27:12 +0000 http://stepansuvorov.com/blog/?p=1401 jQuery Ajax Simple Mock from Cowboy to bookmarks.

]]>
https://stepansuvorov.com/blog/2014/01/jquery-ajax-simple-mock/feed/ 0
jQuery selectors https://stepansuvorov.com/blog/2013/09/jquery-selectors/ https://stepansuvorov.com/blog/2013/09/jquery-selectors/#respond Thu, 05 Sep 2013 12:16:15 +0000 http://stepansuvorov.com/blog/?p=1197 Continue reading ]]> Sometimes it could be quite useful to remind:

Selector Type

jQuery Syntax

Basic
All Selector *
Class Selector .class
Element Selector element
ID Selector #id
Multiple Selector selector1, selector2, selectorN
 
Hierarchy
Child Selector parent > child
Descendant Selector ancestor descendant
Next Adjacent Selector prev + next
Next Siblings Selector prev ~ siblings
Attribute
Attribute Contains Prefix Selector [name|=value]
Attribute Contains Selector [name*=value]
Attribute Contains Word Selector [name~=value]
Attribute Ends With Selector [name$=value]
Attribute Equals Selector [name=value]
Attribute Not Equal Selector(non-standard) [name!=value]
Attribute Starts With Selector [name^=value]
Has Attribute Selector [name]
Multiple Attribute Selector [name=value][name2=value2]
Basic Filter(non-standard)
Animated filter :animated
Not equal filter :not()
Evenness filter :even, :odd
Comparison filter :gt(),:lt(), :eq()
Order filter :first, :last
Header filter :header
Focus filter :focus
Target filter :target
Root Filter :root
Language filter :lang
Child Filter
First/Last child filter :first-child, :last-child
First/Last of type filter :first-of-type, :last-of-type
Nth-child filter :nth-child()
Last Nth-child filter :nth-last-child()
Nth-child of type filter :nth-of-type()
Last nth-child of type filter :nth-last-of-type()
Only child filter :only-child
Only child of type filter :only-of-type
Content Filter
Contains filter :contains()
Empty filter :empty
Has filter :has()
Parent filter :parent
Visibility (non-standard) :hidden, :visible
Form
Checked filter(standard) :checked
Selected filter :selected
Disabled filter(standard) :disabled, :enabled
Input type filter :button, :file, :image, :input, :password, :radio, :checkbox, :reset, :submit, :text

 

Based on

  • http://api.jquery.com/
  • http://www.amplesdk.com/tutorials/jquery/

 

]]>
https://stepansuvorov.com/blog/2013/09/jquery-selectors/feed/ 0