-webkit-box-shadow (the latest Webkit) and-moz-box-shadow support "internal" shadow with inset team. You can also specify multiple shadows through zyapyatuyu.
To obtain such a picture, just write the following:
-webkit-box-shadow (в последнем Webkit) и -moz-box-shadow поддерживают "внутренние" тени вместе с inset командой. Также имеется возможность указать несколько теней через зяпятую.
Чтобы получить такую картинку, достаточно написать следующее:
According to
a poll
conducted by
Money Magazine
and
PayScale.com,
the profession of software developer 4th in the list of most peaceful occupations. In the first place Profession Education / Training Consultant, on the second Physical Therapist, third - College Professor. As the arguments in favor of such a provision of our profession in the rating, cites the following:
1. Free schedule.
2. Ability to work izdomu.
3. Flexibility deadlines smaller subtasks.
But what is particularly nice - technical writers, admins, and even
architects
lose more nerve cells than mere developers.
Take care of your nerves, comrades developers and you enjoy your crisis!
Согласно опросу проведенными
Money Magazine и PayScale.com, профессия разработчика программного обеспечения 4ая в списке наиболее спокойных профессий. На первом месте профессия Education/Training Consultant, на втором Physical Therapist, третьем - College Professor. В качестве аргументов, в пользу такого положения нашей профессии в рейтинге, приводилось следующее:
1. Свободный график работы.
2. Возможность работать издому.
3. Гибкость сроков выполнения небольших подзадач.
Но что особенно приятно - технические писатели, админы и даже архитекторы теряют больше нервных клеток, чем простые девелоперы.
Берегите нервы, товарищи девелоперы и приятного вам кризиса!
Sooner or later there comes a time when opportunities framework you enough. "Oh, how can that be, have forgotten this useful button / control / application to do ..." - Complains you. And then it comes the idea to write the missing functional itself. Precise finish. And any self-respecting framework provides mechanisms for its own expansion / changes. ExtJs was no exception.
In fact, the expansion is a class inherited from the existing library and implementing
additional functional.
Let us imagine that we need to realize Control, very similar in purpose and functionality of Ext.Panel, but with a slight difference - a new panel should always be square (ie the width should always be equal to its height).
1:
SquarePanel = Ext.extend (Ext.Panel, (
2:/ / Set the size of the panel by default
3: width:
100,
4: height:
100,
5: onResize:function
(width, height) (
6:/ / If the size of our panel have been changed, with a disproportionately - to return it to square
Рано или поздно наступает момент, когда возможностей фреймворка вам недостаточно. "Ах, как же так, забыли такую полезную кнопочку/контрол/приложение сделать..." - сетуете вы. Вот тогда и приходит в голову идея написать нехватающий функционал самому. Точнее дописать. И любой уважающий себя фреймворк предоставляет механизмы для собственного расширения/изменения. ExtJs не стал исключением.
По сути расширение это класс, наследованный от уже существующего в библиотеке и реализующий дополнительный функционал. Представим себе, что нам необходимо реализовать контрол, весьма похожий по своему назначению и функционалу на Ext.Panel, но с небольшим отличием - новая панель всегда должна быть квадратной формы (т.е. ширина всегда должна быть равна ее высоте).
1: SquarePanel = Ext.extend(Ext.Panel, {
2://Устанавливаем размер панели по умолчанию
3: width: 100,
4: height: 100,
5: onResize: function(width, height) {
6://Если размеры нашей панели были изменены, причем непропорционально - вернуть ей квадратную форму
7:if (width != height) {
8:this.body.setWidth(height);
9: }
10://Вызываем базовый метод класса-родителя, т.е. - класса Panel.
13://Добавляем метод который позволит нам изменять размеры нашей панели, указав лишь размер одной ее стороны
14: setSquareSize(length){
15:this.body.setWidth(length);
16:this.body.setHeight(length);
17: }
18: });
Вот такая нехитрая конструкция Ext.extend позволяет создать новый класс, с дополнительными возможностями, а именно – новое поведение панели при изменении ее размеров и новый метод setSquareSize позволяющий установить новые размеры панели.Я акцентировал внимание на дополнительных возможностях, потому что все свойства, методы и события базового класса (Ext.Panel) теперь присущи и нашему классу. Если заглянуть внутрь реализации метода extend, то можно увидеть, что
1://создаем пустую функцию-класс F
2:var F = function(){},
3://сюда будет записана ссылка на прототип дочернего класса (т.е. нашего собственного)
4: sbp,
5://сюда будет записана ссылка на прототип родительского класса
6: spp = sp.prototype;
7://прототипом класса F становится прототип родительского класса
8: F.prototype = spp;
9://прототипом нашего же класса становится класс F
10: sbp = sb.prototype = new F();
11://далее идет переопределение конструктора и назначение суперкласса
12: sbp.constructor=sb;
13: sb.superclass=spp;
14: //...много непонятных буков
Методы и свойства, которые мы переопределили (onResize, width и height) попадут в prototype нашего класса вот таким образом:
1://sb - это наш класс SquarePanel, а overrrides - объект содержащий дополнения к нашему классу
2: Ext.override(sb, overrides);
О функции override, я расскажу в другой раз. Пока ограничусь лишь кратким описанием – функция Ext.override позволяет замещать функционал уже существующих классов, на ваши собственные реализации.
Далее мы можем расширять нашу панель точно также как и Ext.Panel
1: DifferentSquarePanel = Ext.extend(SquarePanel, {/* some additional logic here */})
Что ж, довольно просто и элегантно. Впрочем, как и должно быть в любом уважающем себя фреймворке.
Today I begin a series of articles devoted to the framework ExtJs. It will not be Tutorials and manuals for incremental learning library. I just want to share their experiences, so start with something horrible, namely - with the events.
With the advent of
GUI
and the
Mouse,
DOM-objects, like buttons and text fields have become by some events. For example,
Click,mouseover,keyDown,
and others. In fact, the event - the message, the call, generated by one part of the application (the source), which notifies the other part of the application (the receiver or subscriber) that something happened.
1:<divid= "theDiv"onclick= "alert ( 'You clicked me ')">
Click
me!div>
In ExtJs DOM-elements wrapped in
Ext.Element,
so subscribe to the same event will look like this:
Сегодня я начинаю цикл статей посвященных фреймворку ExtJs. Это не будут туториалы или мануалы для пошагового изучения библиотеки. Я просто хочу поделиться своим опытом, поэтому начну с чего попало, а именно – с событий.
С изобретением GUI и Мыши, DOM-объекты, подобные кнопкам и текстовым полям стали обладать некими событиями. Например, click, mouseover, keydown и другими. По сути событие – это сообщение, вызов функции, генерируемое одной частью приложения (источником), которое извещает другую часть приложения (приемник или подписчик) о том, что что-то случилось.
При разработке сложных пользовательских интерфейсов, отдельные элементы DOMа получают особое назначение. К примеру, банальный div может быть заголовком колонки в таблице (я имею ввиду ExtJs Grid). Следовательно, когда пользователь захочет изменить порядок колонок в гриде и начнет перетягивать колонку за заголовок – в заголовке произойдут некие события (mouseover, mousedown, click, …). Каждый раз отслеживать порядок и детали “низкоуровневых” событий в своем коде рутинно. Поэтому ExtJs поддерживает собственную модель событий. К примеру, GridPanel поддерживает такие события как columnmove, cellclick, headerclick и другие.
Вся магия событий в ExtJs реализована в классе Ext.util.Observable. Чтобы наделить ваш компонент свойствами источника события, вам необходимо “унаследоваться” от Ext.util.Observable. Если же вы наследуете свой компонент от других компонентов, таких как Panel, Grid, Form, Tree и другие, то дополнительно наследоваться от Ext.util.Observable не обязательно. О принципе паттерна Observable и об особенностях его реализации на javascript можно прочесть на CodeProject.
После того как вы так или иначе унаследовались от Ext.util.Observable, вы можете добавить ваши собственные события примерно таким образом
Когда мы вызываем функцию fireEvent, то Observable смотрит, подписывался ли на это событие (в данном случае – ‘IAmHappy’) кто-либо или нет. Если подписывался, то вызывает функцию-обработчик и устанавливает значение переменной this равное текущему объекту (этого мы добились указав this вторым параметром после названия события).
Также в эту функцию будут переданы параметры event и target. Где, event будет объект типа Ext.EventObject, а target – ссылка на HTMLElement. Следом за этими параметрами пойдут наши, собственные параметры firstArg и secondArg, коих может быть любое количество.
2: single: true, // удаляет автоматически, после первого запуска
3:delegate: 'li.some-class'// автоматическая делегация событий
4: });
Добавление сразу нескольких обработчиков может выглядеть вот так:
1: el.on({
2:'click' : {
3: fn: this.onClick,
4: scope: this,
5: delay: 100
6: },
7:'mouseover' : {
8: fn: this.onMouseOver,
9: scope: this
10: },
11:'mouseout' : {
12: fn: this.onMouseOut,
13: scope: this
14: }
15: });
Еще стоит упомянуть о такой полезной функции как relayEvents. Она позволяет делегировать события одного компонента – другому компоненту. К примеру, у вас есть панель A, содержащая другую панель B, а та в свою очередь содержит кнопку Save. По нажатию на кнопку Save произойдет ваше собственное событие onSomeSave. К сожалению, ни панель А, ни панель B, обработать это событие не может. Поэтому панели А необходимо генерировать это событие как свое собственное, в надежде, что кто-то сможет его обработать.
“Передать” события панели B в панель A и позволяет relayEvents:
1: panelA.relayEvents(panelB, ['onSomeSave']);
Фух, что-то длинный пост получился. Устал. А я ведь еще не рассказал об Ext.EventManager, о такой полезной функции как within и о многом другом. Ну чтож, это в другой раз.
They announced boarding. A few minutes later the plane Kiev-New York landed at the airport JFK. Most of our aircraft. We are all strangers. But we are united by one thing - the happy faces.
So roughly, and began my journey to the States several months ago. But to write about it, I found the time just now.
Then there was another plane, and so I was in
San Francisco,
California. The first impression of America was in itself a negative case - the case all the difference in mentality. I felt like an alien. But not because I was surprised by the height of skyscrapers, or clean the streets ... Americans - that's what surprised me from the first minute. Banal smile, many times so it is not enough, but when you smile a few hundred people almost simultaneously, anywhere on Broadway or the 5th, 7th, and any other avenues. Really unpleasant. Do not know what to do in response. The same smile? I can not smile during the day. We - the Soviet people (yes, I was born in the USSR!) - Developed the wrong muscles of the face. I tried. By evening smile sticks, face perekosobochivaet, the smile turns deadly wound nnogo rights.
Ocean.
The next thing that made a strong impression on me. At the weekend I got to the ocean. On the occasion of the month of July, no one bathed, only amused a few surfers in their respective costumes. And I popularly explained that all along the Pacific coast of North America is a cold current, so the water temperature does not rise above 18-23 C. The only people who are not embarrassed by such a low temperature of the water, were - children. They swam and behaved as well as we, on the beaches of the Crimea - something dug, looked for something to surf somewhere and the water buckets were built unimaginable structure of the sand.
Americana said: "America has made
the car."
This is understandable. Most of the life they spend in cars. Machines are good, no words, but not less good roads on which these vehicles travel. Concrete slabs, stacked in a butt joint, almost without cracks. Some sections of roads - paid (including bridges). What is a toll road? This is the shortest way to your destination, with excellent high-speed mode and bandwidth (due to the quality of the coating). Corks have, but with no ...
Объявили посадку. Через несколько минут самолет Киев-Нью Йорк приземлился в аэропорту JFK. Большинство в самолете наши. Мы все незнакомы. Но нас объединяет одно - радостные лица.
Так примерно и начиналось мое путешествие в Штаты несколько месяцев назад. Но написать об этом, я нашел время только сейчас.
Потом был еще один самолет и так я оказался в San Francisco, California. Первое впечатление об Америке было на само деле отрицательным - дело все в разнице менталитета. Я чувствовал себя инопланетянином. Но не потому что был удивлен высотой небоскребов или чистотой улиц... Американцы - вот, что меня удивило с первой минуты. Банальная улыбка, многим ее иногда так не хватает, но когда тебе улыбается несколько сотен человек практически одновременно, где-нибудь на Бродвее или 5й, 7й, да и любой другой авеню. Действительно неприятно. Не знаешь, что делать в ответ. Тоже улыбаться? Я не могу улыбаться в течение суток. У нас - советских людей (да, я рожден в СССР!) - развиты не те мышцы лица. Я пробовал. К вечеру улыбку заклинивает, лицо перекособочивает, получается улыбка смертельно ране нного человека.
Океан. Следующее, что произвело на меня сильное впечатление. На ближайшие выходные я добрался до океана. По случаю июля месяца, никто не купался, только баловались несколько серфингистов в соответствующих костюмах. И мне популярно объяснили, что вдоль всего тихоокеанского побережья Северной Америки идет холодное течение, поэтому температура воды не поднимается выше 18-23 C. Единственные, кого не смущала столь низкая температура воды, были - дети. Они купались и вели себя также как и у нас, на пляжах Крыма - чего-то рыли, что-то искали в прибое, куда-то носили ведёрками воду и строили немыслимые строения из песка.
Американы говорят: "Америку сделал автомобиль". Это понятно. Большую часть жизни они проводят в авто. Машины хороши, нет слов; но не менее хороши дороги, по которым эти машины ездят. Бетонные плиты, уложенные стык в стык, почти без трещин. Некоторые участки дорог – платные (в частности мосты). Что есть платная дорога? Это кратчайший путь к вашей цели, с прекрасным скоростным режимом и пропускной способностью (за счет качества покрытия). Пробки есть, но с ними активно борятся – появились так называемые Car pool. Это полосы по которым можно ехать если в машине 2 и более человек. А если кто-то сломался, его тут же стаскивают с дороги (есть служба специально для этого). При въезде на платную дорогу вы даете кредитку и въезжаете на дорогу (на некоторых участках просто фоторафируют ваш номер и присылают раз в месяц счет за проезд). Занимает секунды. Как я узнал позже, номера имеют не только улицы, но и дороги, и вся Америка разбита на некие сектора под номерами. Если вы хотите уйти от полиции, это будет сделать непросто. Стукачей полно, и все сектора (дороги) под номерами. Даже сами американцы имеют свои номера. Это называется номер социального страхования. Вся их жизнь - это сплошное громадье цифр, номеров, счетов. Заблудиться почти невозможно. Останавливаетесь, смотрите карту, номер сектора дороги, где вы сейчас, и ориентируетесь моментально.
Как вы понимаете, в Штатах я оказался не отдыха ради, а по работе. О деталях проекта, по понятным причинам, я рассказать ничего не могу. Скажу лишь, что в ближайшее время вы сможете увидеть посты на тему Enterprise Patterns, различных особенностей SOA архитектур, пару слов об ExtJs, а также о войнах браузеров. Часто писать не обещаю – много работы, а релиз как всегда запланирован на вчера ;)
В целом, у меня сложилось впечатление, что Америка – это страна, где меньше думают и больше действуют. При решении любого вопроса американцы стремятся как можно раньше начать действовать, даже совершая при этом ошибки. Чтож, в этом есть определенный смысл. Особенно в мире веб - где скорость выпуска продукта прямо пропорциональна количеству “завоеванных” пользователей.
P.S.
Теперь ваши комментарии будут появляться в блоге, лишь спустя 12 часов – апрувить я смогу их лишь своим ранним утром. Не обессудьте.
You bearded web developer and you think you have seen all kinds of miracles in the JavaScript-code? Hmm, then what have these puzzles for you.
1. + + Math.PI
2. (0.1 + 0.2) + 0.3 == 0.1 + (0.2 + 0.3)
3. typeof NaN
4. typeof typeof undefined
5. a = (null: null); typeof a.null;
6. a = "5"; b = "2"; c = a * b;
7. a = "5"; b = 2; c = a + + + b;
8. isNaN (1/null)
9. (16). ToString (16)
10. 016 * 2
11. ~ null
12. "ab c". match (/ b w b /)
Waiting for your options in the comments (please do not fast on ten identical comments, it is still earlier than I promoderyu them they will not appear). Until companies, I'll get tomorrow, so your comments, too, will be back tomorrow, and along with an update to this post - in the form of answers and explanations for decisions (yes, I accompanied them with your comments, so at first think, then write!)
Answers and solutions:
1. 4.141592653589793
With this hope is understandable, in school all went .... I know some embarrassing, that sort of Pi must be a constant, but I hasten to disappoint you - in dzhavaskripte constants bad, but rather they are not in the sense in which we see them, say, C #.
2. false
The same answer given and Java developers, because as Java and JavaScript using
IEEE 754 floating point numbers. More information about operations with floating point numbers can be found
tutanki.
3. "number"
Logical explanation that I have not found, the only thing that I can advise is try to use the isNaN instead of typeof SomePotentialNumber, in order to avoid mishaps.
4. "string"
Everything is simple - the first typeof operator returns the type of a variable as a string, hence the second statement should return the result type of the first operator, ie string.
5. "object"
We assigned a variable, an object with a field "null", whose value is null, and this, in turn, albeit specific, but the object. In other words, we here have
a.null === null.
6. 10
If the operation of multiplication, division or subtraction of one of the operators line - the interpreter tries to convert it to a number.
Вы бородатый веб разработчик и думаете уже видали всякие чудеса в JavaScript-коде? Хм, чтож тогда следующие задачки для вас.
1. ++Math.PI 2. (0.1 + 0.2) + 0.3 == 0.1 + (0.2 + 0.3) 3. typeof NaN 4. typeof typeof undefined 5. a = {null:null}; typeof a.null; 6. a = "5"; b = "2"; c = a * b; 7. a = "5"; b = 2; c = a+++b; 8. isNaN(1/null) 9. (16).toString(16) 10. 016 * 2 11. ~null 12. "ab c".match(/\b\w\b/)
Жду ваши варианты в комментариях (просьба не постить по десять одинаковых комментариев, все равно раньше чем я их промодерю они не появятся). До компа я доберусь только завтра, поэтому и ваши комментарии тоже появятся только завтра, а заодно и обновление к этому посту - в виде ответов и объяснений к решениям (да, я их сопру с ваших комментариев, поэтому сначала думайте, а потом пишите!)
Ответы и решения:
1. 4.141592653589793
С этим надеюсь понятно, в школе все учились.... Я знаю некоторых смутило, что Пи вроде как должно быть константой, но спешу вас разочаровать - в джаваскрипте с константами плохо, а точнее их нет в том понимании, в каком мы видим их, скажем, в C#.
2. false
Такой же ответ получат и Java разработчики, потому как и Java и JavaScript используют IEEE 754 числа с плавающей точкой. Более подробно об операциях с числами с плавающей точкой можно найти тутаньки.
3. "number"
Логического объяснения этому я не нашел, единственное что могу посоветовать, это стараться использовать isNaN вместо typeof SomePotentialNumber, во избежание казусов.
4. "string"
Тут все просто - первый оператор typeof вернет тип переменной в виде строки, следовательно второй оператор должен вернуть тип результата первого оператора, т.е. string.
5. "object"
Мы присвоили переменной а, объект с полем "null", значение которого null, а это в свою очередь, пусть и специфический, но объект. Другими словами мы тут имеем a.null === null.
6. 10
Если в операциях умножение, деление или вычитание один из операторов строка - интерпретатор пытается преобразовать его в число.
7. 7
Порядок выполнения операндов в Javascript идет слева направо, поэтому мы имеем "а увеличить на 1, а затем сложить с b".
8. false
С этим тоже, думаю, все понятно.
9. 10
toString() принимает числовой параметр (необязательный) и toString(16) означает, что число записано в шестнастеричной системе счисления, посему ответ и 10.
10. 28
016 это восьмеричное представление числа 14 в десятеричной системе счисления.
11. -1
12. [ "c" ]
Отдельно стоящий символ в той строке только один - "с", это и ищет указанное регулярное выражение.
I think all already know that
we have a crisis
(now it seems financial), however like all. And everyone understands that nothing good to him this event will bring. And one of the "benefits" of the crisis is the minimum wage, ie the amount of money for which you do not mind to work and the boss to fire you no reason. Sounds great, right? True. For most people, well-developed sense of possessiveness, so long as it's not mine, I do not care who owns it, but as soon as it was my - fuck who it is, I will choose! And here is the deal - lower wages! I will not be distributed on the tree here is instructive comic on this subject:
Думаю все уже в курсе, что у нас кризис (теперь кажется финансовый), впрочем как и у всех. И каждый понимает что ничего хорошего ему это событие не принесет. А одним из "благ" кризиса является минимальная зарплата, т.е. та сумма денег, за которую и вы не против поработать и шефу увольнять вас пока нет повода. Звучит неплохо, правда? Неправда. У большинства людей хорошо развито чувство собственничества, поэтому пока это не мое, мне все равно кто этим владеет, но как только оно стало моим - хрен кто это у меня отберет! А тут такое дело - зарплату понижают! Не буду больше растекаться по древу вот вам поучительный комикс на эту тему:
1. Ajax 101 | Workshop
Author:
Bill Scott
|
This presentation on SlideShare
Introduction to programming with Ajax. Includes XMLHttpRequest, XML, JSON, JavaScript, HTML, CSS, Dom Scripting, Event Handling with a few examples of YUI.
2. Modular CSS
Author:
Russ Weakley
|
This presentation on Slide Share
It is available (even I realized) due to the mechanism of constructing a
regular
modular CSS, that allows you to hide / show some CSS rules for specific browsers, without different kinds of tricks and detours.
3. jQuery in 15 minutes
Author:
Simon
|
This presentation on SlideShare
A small introduction to JQuery. Functions, collections, working with values and chains.
1. Ajax 101 | Workshop Автор: Bill Scott | This presentation on SlideShare Введение в программирование с помощью Ajax. Включает XMLHttpRequest, XML, JSON, JavaScript, HTML, CSS, Dom Scripting, Event Handling с небольшими примерами на YUI.
2. Modular CSS Автор: Russ Weakley | This presentation on Slide Share Вполне доступно (даже я понял) объясняется механизм построения правильного модульного CSS, что позволяет прятать/показывать отдельные CSS правила для отдельных браузеров, без различного рода уловок и обходных путей.
3. jQuery in 15 minutes Автор: Simon | This presentation on SlideShare Небольшое введение в JQuery. Функции, коллекции, работа со значениями и цепочками.
4. JavaScript Library Overview Автор Jeresig | This presentation on SlideShare Интересный обзор популярных JavaScript библиотек (jquery, prototype, Scriptaculous...) для веб-дизайнеров.
Long been known that the operation with DOM 'th very, very cumbersome. Losses in productivity is usually visible in three cases:
when the script performs the
manipulation
of the tree of objects (creates, deletes, or revises part of the tree)
if the script "forces " browser
to redraw
(redraw) or
rebuild the layout
(reflow) Pages
and finally, in the case when the script "looking " one of the nodes in the tree of objects (if the tree is large).
The last point I have discussed in
an article in
its cycle
Skazheni Kabani,
for example, JQuery. So today we'll talk about the first two. Em ... actually I cheat, and the first paragraph is nothing but a cause of the second paragraph. Then immediately proceed to the second paragraph and try to understand in terms of the redraw and re-counting:
Redraws the
Browser occurs when something has changed visually, but the page layout remains the same. For example, change the color of an element or element is visible / invisible (with visibility: [hidden, visible], because it does not affect the counting). This operation significantly affects the performance of Web applications, as well as makes the browser go on a tree of objects and determine what elements are visible and how they should be displayed.
Restructuring page layout more expensive operation. It occurs in the following cases:
when you first load the page. In the case of Firefox, the restructuring may occur several times, as far as
resuming the
page's
content;
when you add or remove items DOM 'a. I must say, there is one exception - if you add / remove an object with absolute positioning, it may not lead to a restructuring of the markup page as well as the position and size of other elements have not changed;
when the style of a changed and it affects the size and position of this or other objects;
when you try to apply to properties that require calculations of the browser (for example, offsetWidth, clientHeight). And also in case of attempts to obtain the calculated values of CSS (using getComputedStyle () or currentStyle in IE).
Давно известно, что операции с DOM'ом весьма и весьма трудоемки. Потери в производительности заметны обычно в трех случаях:
когда скрипт выполняет манипуляции с деревом объектов (создает, удаляет или изменяет часть дерева)
если скрипт "заставляет" браузер перерисовывать (redraw) или перестраивать разметку (reflow) страницы
и наконец, в случае когда скрипт "ищет" один из узлов дерева объектов (если дерево большое).
Последний пункт я уже рассматривал в одной из статей своего цикла Скаженi кабани, на примере JQuery. Поэтому сегодня мы поговорим о первых двух. Эм...на самом деле я схитрил, и первый пункт представляет собой ни что иное, как причину появления второго пункта. Тогда сразу перейдем ко второму пункту и попробуем разобраться в терминах перерисовывать и перестраивать разметку:
Перерисовка страницы браузером происходит в случае, когда что-то визуально изменилось, но разметка страницы осталась прежней. Например, изменился цвет элемента или элемент стал видимым/невидимым (с помощью visibility: [hidden, visible], так как это не повлияет на разметку). Эта операция существенно влияет на производительность веб приложения, так как заставляет браузер пройтись по дереву объектов и определить какие элементы видимы и как они должны быть отображены.
Перестройка разметки страницы более дорогостоящая операция. Она происходит в следующих случаях:
при первой загрузке страницы. В случае с Firefox, перестройка может происходить несколько раз, по мере докачивания контента страницы;
когда вы добавляете или удаляете элементы DOM'a. Надо сказать, тут есть одно исключение - если вы добавили/удалили объект с абсолютным позиционированием, то это может и не привести к перестройке разметки страницы, так как позиция и размеры других элементов не были изменены;
когда стиль элемента изменен и он влияет на размер и положение этого либо других объектов;
когда вы пытаетесь обратиться к свойствам, требующим вычислений со стороны браузера (например, offsetWidth, clientHeight). А также в случае попытки получить вычисляемые CSS значения (с помощью getComputedStyle() или currentStyle в IE).
Процесс перестройки разметки страницы выглядит примерно следующим образом:
Но перестройка разметки странцы не происходит сразу после добавления элемента в дерево или после изменения высоты элемента. Все "запросы" на перестройку разметки выстраиваются в очередь и выполняются уже после выполнения скрипта. Причем, некоторые "запросы" могут быть объединены, если они применяются к одному элементу и являются одного типа (например изменяют его ширину). Но процесс оптимизации таких "запросов" сильно зависит от типа браузера (и даже версии браузера). Пользователь не может взаимодействовать с вашим приложением, пока идет перестройка разметки.
Теперь когда мы в курсе "почему наш сайт тормозит", рассмотрим несколько техник оптимизации работы с DOM'ом. Первое что приходит в голову, это минимизировать количество дорогостоящих операций (описанных выше). А значит, необходимо как можно больше операций совершать вне DOM'а, например используя DocumentFragment.
var products = ... //init array of products var list = document.getElementById("myProducts"); //find list populate to for (var i=0; i < products.length; i++){ var item = document.createElement("li");
item.appendChild(document.createTextNode("Product" + products[i]); list.appendChild(item); // AHTUNG! Operation with DOM }
Предыдущий код можно оптимизировать следующим образом:
var products = ... //init array of products var list = document.getElementById("myProducts"); //find list populate to var fragment = document.createDocumentFragment(); //create document fragment for (var i=0; i < products.length; i++){ var item = document.createElement("li"); item.appendChild(document.createTextNode("Product" + products[i]); fragment.appendChild(item); //working with fragment only, not with DOM } list.appendChild(fragment); //add fragment to DOM
Эта версия кода затрагивает дерево объектов только раз, в последней строчке. И так как DocumentFragment не имеет визуальной составляющей, то все операции с ним не вызывают ни перерисовки, ни изменения в разметке страницы. Но так как DocumentFragment не может быть добавлен в DOM, то операция appendChild добавит все дочерние элементы фрагмента (вместо того, чтобы добавить сам фрагмент).
Другим, более эффективным подходом, будет работать с элементом, не находящимся в дереве. К примеру, мы можем удалить наш объект из дерева перед выполнением операций над ним (removeChild() или replaceChild())
var products = ... //init array of products var list = document.getElementById("myProducts"); //find list populate to varparent = list.parentNode; //find parent parent.removeChild(list); //remove list element from DOM var fragment = document.createDocumentFragment(); //create document fragment for (var i=0; i < products.length; i++){ var item = document.createElement("li"); item.appendChild(document.createTextNode("Product " + products[i]); list.appendChild(item); } parent.appendChild(list);
Мы не избежали перестройки разметки страницы, но мы уменьшили количество таких перестроек.
Другой причиной перерисовки или перестройки страницы служат стили и спобосы их назначения элементам. Рассмотрим следующий кусок кода:
element.style.backgroundColor = "white"; //will cause redraw element.style.color = "red"; //will cause redraw element.style.fontSize = "12em"; //will cause reflow element.style.widht = "100px"; //will cause reflow
Как видите первые две строки инициируют перерисовку. Избежать этого можно, если перед изменением стилей скрыть элемент с помощью visibility:hidden или display:none (будет произведено два лишних reflow). Избежать перестройки разметки тут не получиться, но их количество можно уменьшить, если задать все изменения стилей в CSS классе.
Скрытый reflow может также произойти, если мы попытаемся получить одно из вычисляемых свойств (например offsetWidth). Браузер должен быть уверен, что значение свойства актуально, поэтому он рассчитает его заново, а это фактически и будет перестройка разметки страницы. Избежать этого опять же нельзя, но мы можем уменьшить количество таких операций за счет кэширования значения этого свойства: