Javascript.RU

Кросс-браузерное добавление и обработка событий

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/introduction-browser-events.

В этой статье мы создадим мини-библиотеку, которая будет кросс-браузерно работать с событиями.

Ее задача - навешивать/убирать обработчики, а также делать одинаковой работу с ними для разных браузеров.

Основные требования: простота, грамотность и компактный код.

Те же требования, более подробно:

Изначальные требования:

  • Нужны функции для добавления и удаления обработчика.
  • Решение должно одинаково работать в IE6+, Firefox(Gecko), Opera и Safari
  • Код должен быть достаточно коротким, понятным и простым
  • Обработчику должен корректно передаваться объект события event и текущий элемент this.
  • Утечки памяти должны быть минимальны.

Наибольшее распространение в интернете получил следующий "универсальный" код.

function addEvent(elem, type, handler){
  if (elem.addEventListener){
    elem.addEventListener(type, handler, false)
  } else {
    elem.attachEvent("on"+type, handler)
  }
} 
// ..и аналогичный метод removeEvent

Он, в самом деле, очень прост. Однако, есть и недостатки.

  1. Несколько обработчиков. Просто.
  2. В IE будет некорректно передан текущий элемент(this).
  3. Обработчик handler должен самостоятельно производить кросс-браузерную предобработку события

Передать текущий элемент в IE можно через замыкание: elem.attachEvent("on"+type, function() { handler.call(elem) }), что, впрочем, приведет к утечкам памяти в IE6 до апдейта июня 2007 года.

Современные яваскрипт-библиотеки, как правило, не используют этот код.

На текущий момент более-менее устоялся стандартный способ кросс-браузерного добавления событий.

Большой вклад в это внесли Dean Edwards и Tino Zeidel, на библиотеках которых зачастую и основан современный код. Например, это так для dojo toolkit и jQuery.

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

  1. Обработчики срабатывают в том же порядке, в котором назначены
  2. Обработчик может остановить не только всплытие, но и дальнейшую цепочку обработки на текущем элементе
  3. Можно получить список обработчиков
  4. Можно удалить все обработчики события/элемента, не обладая ссылкой на каждую функцию
  5. ...

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

Основная логика работы:

  1. Элементу добавляется объект events, который содержит назначенные обработчики событий.

    Например, добавим чистому элементу elem события:

    /* Event - наша новая библиотека */
     
    Event.add(elem, "click", function(e) { alert("Hi") })
    Event.add(elem, "click", function(e) { alert("I am clicked") })
    Event.add(elem, "mouseover", function(e) { alert("Mouse over!") })
    

    Это создаст следующий служебный объект:

    elem.events = {
      'click' : {
        1 : function(e) { alert("Hi!") },
        2 : function(e) { alert("I am clicked") }
      },
      'mouseover' : {
        3 : function(e) { alert("Mouse over!") }
      }
    }
    

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

  2. При добавлении первого обработчика некоторого события, функция Event.add при помощи addEventListener/attachEvent вешает на это событие специальный служебный обработчик handle. Обработчик создается так, чтобы помнил элемент, на котором висит.

    Теперь при наступлении события браузер запустит функцию-обработчик handle.
    А уже она, зная свой элемент, получает объект events и запускает назначенные обработчики из списка.

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

    Что немаловажно, handle не просто запускает обработчики, а еще и осуществляет кроссбраузерную обработку объекта события event, так что не надо беспокоиться о различиях между браузерами.

Теперь, когда у нас есть общий формат, и в общих чертах понятно, как оно будет работать - займемся деталями реализации.

Мини-библиотечка Event будет представлять собой синглтон с несколькими приватными и двумя публичными функциями:

add(elem, type, handle)
добавить обработчик handle для события type на элементе elem
remove(elem, type, handle)
убрать обработчик события
Event = (function() {

  // текущий номер обработчика
  var guid = 0

  function fixEvent(event) {
    // кросс-браузерная предобработка объекта-события
    // нормализация свойств и т.п.
  }

  function commonHandler(event) {
    // вспомогательный универсальный обработчик 
  }
  
  return {
    add: function(elem, type, handler) {
      // добавить обработчик события
    },
    
    remove: function(elem, type, handler) {
      // удалить обработчик события
    }
      
  }
}())

Добавление обработчика осуществляется функцией add:

add: function(elem, type, handler) {
  // исправляем небольшой глюк IE с передачей объекта window
  if (elem.setInterval && ( elem != window && !elem.frameElement ) ) {
	elem = window;
  }
  
  // (1)
  if (!handler.guid) {
    handler.guid = ++guid
  }
  
  // (2)
  if (!elem.events) {
	elem.events = {}
	
	elem.handle = function(event) {
	  if (typeof Event !== "undefined") {
		return commonHandle.call(elem, event)
	  }
	}
  }
  
  // 3  
  if (!elem.events[type]) {
    elem.events[type] = {}        
  
    if (elem.addEventListener)
      elem.addEventListener(type, elem.handle, false)
    else if (elem.attachEvent)
      elem.attachEvent("on" + type, elem.handle)
  }
  
  // (4)
  elem.events[type][handler.guid] = handler
}

Вот расшифровка того, что она делает:

  1. Назначить функции-обработчику уникальный номер. По нему обработчик можно будет легко найти в списке events[type].
  2. Инициализовать служебную структуру events и обработчик handle.

    Обработчик handle фильтрует редко возникающую ошибку, когда событие отрабатывает после unload'а страницы.

    Основная же его задача - передать вызов универсальному обработчику commonHandle с правильным указанием текущего элемента this.

    Как и events, handle достаточно инициализовать один раз для любых событий.

  3. Если обработчиков такого типа событий не существует - инициализуем events[type] и вешаем elem.handle как обработчик на elem для запуска браузером по событию type.
  4. Добавляем пользовательский обработчик в список elem.events[type] под заданным номером.

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

    function handleIt(e) { ... }
    Event.add(elem, type, handleIt)
    Event.add(elem, type, handleIt)
    // добавился 1 обработчик handleIt
    

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

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

Вспомогательная служебная функция-обработчик:

function commonHandle(event) {
  // (1)
  event = fixEvent(event)
  
  // (2)
  var handlers = this.events[event.type]

  for ( var g in handlers ) {
    // (3)
    var ret = handlers[g].call(this, event)
    // (4)
    if ( ret === false ) {
        event.preventDefault()
        event.stopPropagation()
    }
  }
}
  1. Осуществляет получение и предобработку объекта события.
  2. Получает список обработчиков.
  3. По очереди запускает обработчики в контексте текущего элемента.
  4. Правильно обрабатывает return false из обработчика

Теперь уже достаточно просто написать удаление обработчика:

remove: function(elem, type, handler) {
  // (1)
  var handlers = elem.events && elem.events[type]  
  if (!handlers) return
  
  // (2)
  delete handlers[handler.guid]
  
  // (3)
  for(var any in handlers) return
  // (3.1)
  if (elem.removeEventListener)
    elem.removeEventListener(type, elem.handle, false)
  else if (elem.detachEvent)
    elem.detachEvent("on" + type, elem.handle)
    
  delete elem.events[type]

  // (3.2)
  for (var any in elem.events) return
  try {
    delete elem.handle
    delete elem.events 
  } catch(e) { // IE
    elem.removeAttribute("handle")
    elem.removeAttribute("events")
  }
}
  1. Получить список обработчиков
  2. Удалить обработчик по его номеру
  3. Проверить, не пуст ли список обработчиков
    1. Если пуст, то удалить служебный обработчик и очистить служебную структуру events[type]
    2. Если событий вообще не осталось - удалить events и handle за ненадобностью. IE может выдать ошибку при delete свойства элемента, поэтому для него предусмотрен блок catch.

Ряд свойств объекта события следует привести к кросс-браузерному, удобному для использования виду.

Как это делать для каждого свойства - подробно описано в статье о свойствах объекта события.

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

Осторожно: readOnly

Почти все свойства объекта события read only, то есть доступны только для чтения.

Поэтому при добавлении возможно отсутствующих свойств в объект события нельзя написать такой код:

event.target = event.target || event.srcElement

По замыслу он должен ставить свойство target, когда его нет (IE), а по факту - будет вываливаться с ошибкой в Firefox, т.к target там уже присутствует. В этом случае выполнится присваивание event.target = event.target, что недопустимо, т.к target - readOnly.

Правильный вариант:

if (!event.target) {
    event.target = event.srcElement
}
function fixEvent(event) {
  // получить объект события
  event = event || window.event
  
  // один объект события может передаваться по цепочке разным обработчикам
  // при этом кроссбраузерная обработка будет вызвана только 1 раз
  if ( event.isFixed ) {
    return event
  }
  event.isFixed = true // пометить событие как обработанное

  // добавить preventDefault/stopPropagation для IE
  event.preventDefault = event.preventDefault || function(){this.returnValue = false}
  event.stopPropagation = event.stopPropagaton || function(){this.cancelBubble = true}
  
  // добавить target для IE
  if (!event.target) {
      event.target = event.srcElement
  }

  // добавить relatedTarget в IE, если это нужно
  if (!event.relatedTarget && event.fromElement) {
      event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
  }

  // вычислить pageX/pageY для IE
  if ( event.pageX == null && event.clientX != null ) {
      var html = document.documentElement, body = document.body;
      event.pageX = event.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0);
      event.pageY = event.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0);
  }

  // записать нажатую кнопку мыши в which для IE
  // 1 == левая; 2 == средняя; 3 == правая
  if ( !event.which && event.button ) {
      event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
  }
  
  return event
}

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

Event = (function() {

  var guid = 0
    
  function fixEvent(event) {
	event = event || window.event
  
    if ( event.isFixed ) {
      return event
    }
    event.isFixed = true 
  
    event.preventDefault = event.preventDefault || function(){this.returnValue = false}
    event.stopPropagation = event.stopPropagaton || function(){this.cancelBubble = true}
    
    if (!event.target) {
        event.target = event.srcElement
    }
  
    if (!event.relatedTarget && event.fromElement) {
        event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
    }
  
    if ( event.pageX == null && event.clientX != null ) {
        var html = document.documentElement, body = document.body;
        event.pageX = event.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0);
        event.pageY = event.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0);
    }
  
    if ( !event.which && event.button ) {
        event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
    }
	
	return event
  }  
  
  /* Вызывается в контексте элемента всегда this = element */
  function commonHandle(event) {
    event = fixEvent(event)
    
    var handlers = this.events[event.type]

	for ( var g in handlers ) {
      var handler = handlers[g]

      var ret = handler.call(this, event)
      if ( ret === false ) {
          event.preventDefault()
          event.stopPropagation()
      }
    }
  }
  
  return {
    add: function(elem, type, handler) {
      if (elem.setInterval && ( elem != window && !elem.frameElement ) ) {
        elem = window;
      }
      
      if (!handler.guid) {
        handler.guid = ++guid
      }
      
      if (!elem.events) {
        elem.events = {}
		elem.handle = function(event) {
		  if (typeof Event !== "undefined") {
			return commonHandle.call(elem, event)
		  }
        }
      }
	  
      if (!elem.events[type]) {
        elem.events[type] = {}        
      
        if (elem.addEventListener)
		  elem.addEventListener(type, elem.handle, false)
		else if (elem.attachEvent)
          elem.attachEvent("on" + type, elem.handle)
      }
      
      elem.events[type][handler.guid] = handler
    },
    
    remove: function(elem, type, handler) {
      var handlers = elem.events && elem.events[type]
      
      if (!handlers) return
      
      delete handlers[handler.guid]
      
      for(var any in handlers) return 
	  if (elem.removeEventListener)
		elem.removeEventListener(type, elem.handle, false)
	  else if (elem.detachEvent)
		elem.detachEvent("on" + type, elem.handle)
		
	  delete elem.events[type]
	
	  
	  for (var any in elem.events) return
	  try {
	    delete elem.handle
	    delete elem.events 
	  } catch(e) { // IE
	    elem.removeAttribute("handle")
	    elem.removeAttribute("events")
	  }
    } 
  }
}())

Следующий код добавляет небольшой обработчик:

function handler(event) {
	this.innerHTML = "event.pageX="+event.pageX
}

Event.add(elem, 'click', handler)
// и никакого дополнительного кросс-браузерного кода

.. на этот div...

Кликните здесь

Как видно, корректно передано событие, this и кроссбраузерно установлена координата мыши event.pageX

Удаление:

Event.remove(elem, 'click', handler)

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

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

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

for (var g in handlers ) {
    var handler = handlers[g]

    var ret = handler.call(elem, event)
    if ( ret === false ) {
        event.preventDefault()
        event.stopPropagation()
    }

    **if (event.stopNow) break**
}

В jQuery аналогичный флаг ставится методом event.stopImmediatePropagation().

Для передачи значения дальше по цепочке, обработчик может записать его в объект event:

event.currentResult = myValue

Эту логику можно добавить в общий цикл вызова обработчиков. Любое возвращаемое значение, кроме false будет записываться как event.result:

if ( ret === false ) {
        event.preventDefault()
        event.stopPropagation()
    } else if ( ret !== undefined) {
        event.result = ret
    }

Для удаления всех обработчиков определенного типа, или вообще - всех обработчиков для элемента, функция remove имеет все необходимое.

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

remove: function(elem, type, handler) {
    var handlers = elem.events && elem.events[type]
      
    if (!handlers) return

    if (!handler) {
        for ( var handle in handlers ) {
            delete events[type][handle]
        }
        return
    }

    // остальная часть функции - без изменений 
    delete handlers[handler.guid]
    // ...
}

Наличие структуры events позволяет получить назначенные обработчики.

Полностью очистить элемент от обработчиков можно в 3 счета:

  1. Удалить elem.events
  2. Запустить removeEventListener/detachEvent для elem.handle
  3. Удалить elem.handle. Все, элемент чист.

Стандартный способ избавления от утечек - записывать все элементы, у которых есть события, в специальный объект toClobber, а затем, при unload страницы или при удалении элемента из DOM - очищать эти элементы(и их потомки) от обработчиков.

Этот подход (или его вариант) реализован практически во всех современных яваскрипт-библиотеках.

Остальные браузеры, как и сам IE6 с этим исправлением (ставится автоматически microsoft update) в этом не нуждаются.

Это - о реальной утечке, при которой память не освобождается при переходе на следующую страницу.

Если вы пишете сложное AJAX-приложение, в котором посетитель долго остается на одной странице, то могут иметь место псевдоутечки, при которых память не освобождается просто потому, что объект окончательно не убит, а находится "в зоне видимости" интерпретатора.

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

В этом смысле сборщик мусора в браузерах работает так же, как в Java/PHP5+/Python и других современных языках программирования. Разница в том, что в яваскрипт нечаянную ссылку легко создать при помощи замыкания.

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

Мы получили простую удобную библиотеку для кросс-браузерной работы с событиями.

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


Автор: Гость (не зарегистрирован), дата: 30 апреля, 2009 - 21:49
#permalink

2 важных недостатка:
1. это получаются не обработчики событий, а их callback (dean edwards callbacks vs events)
2. я не могу использовать один и тот же обработчик для разных событий, так как guid будет перезаписываться


Автор: Илья Кантор, дата: 1 мая, 2009 - 09:50
#permalink

1. Поясню отличие для читателей комментария. Callback (в смысле Dean'а) - означает, что один инициировавший исключение обработчик события не дает выполняться следующим. А события - это когда обработчики выполняются независимо от результата друг друга.

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

Поэтому такое поведение это не недостаток, а преимущество. IMO.

2. все будет нормально, guid не перезаписывается.


Автор: Гость (не зарегистрирован), дата: 1 мая, 2009 - 12:41
#permalink

1.
вообщем обработчики событий на то так и устроены, чтоб вызывались независимо, и маленькая ошибка, которая может возникнуть, остановит все

в общем этот скрипт можно модифицировать


Автор: Илья Кантор, дата: 2 мая, 2009 - 10:41
#permalink

Описал проблему и пути решения в новой статье про неустойчивость системы обработчиков к ошибкам.


Автор: x-yuri, дата: 1 мая, 2009 - 17:02
#permalink

есть мнение, что в статье ошибка
http://javascript.ru/forum/events/3497-oshibka-pri-dobavlenii-obrabotchi...


Автор: Илья Кантор, дата: 2 мая, 2009 - 00:02
#permalink

Спасибо, исправлено.


Автор: Riim, дата: 14 августа, 2009 - 12:10
#permalink

Возможно, нужно еще кое-что поправить: http://javascript.ru/forum/events/4685-pomogite-oshibka-v-6-osle-xmlhttprequest.html#post26788


Автор: MaxPayne, дата: 6 мая, 2009 - 08:16
#permalink

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

ФФ выдает ошибки, что не определены функции Event.observe и Event.extend


Автор: Илья Кантор, дата: 6 мая, 2009 - 10:35
#permalink

Эта библиотечка несовместима с Prototype. Чтобы стала совместима - переименуй синглтон с Event.. Скажем, на EventSimple.


Автор: MaxPayne, дата: 6 мая, 2009 - 22:28
#permalink

Что такое синглтон?

Update: Поменял. Все работает и не конфликтует. Может добавить это уточнение в статью, чтобы другие могли видеть? (многие до комментариев не доходят)


Автор: Илья Кантор, дата: 6 мая, 2009 - 22:51
#permalink

Если вы и так загружаете библиотеку Prototype на странице - пользуйтесь ее собственным Event.


Автор: MaxPayne, дата: 6 мая, 2009 - 23:27
#permalink

Где-то читал, что ее собственный Event - одно из самых слабых мест библиотеки.


Автор: Илья Кантор, дата: 8 мая, 2009 - 00:26
#permalink

Да, так было.

Актуальная информация следующая: в современном prototype (1.6.x на момент написания этих строк) для управления обработчиками используется технология, описанная в этой статье.


Автор: EshkinKot (не зарегистрирован), дата: 16 июля, 2010 - 19:14
#permalink

Что такое синглтон?

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


Автор: Корниенко Виталий (не зарегистрирован), дата: 12 июня, 2009 - 22:29
#permalink

Как добавить или совместить к примеру событие загрузки ДОМ.
Вот к примеру можно использовать этот объект:
DOMReady=function(A){var C=false;ready=function(){if(!C){A();C=true}};try{document.addEventListener("DOMContentLoaded",ready,false)}catch(B){timer=setInterval(function(){if(/loaded|complete/.test(document.readyState)){clearInterval(timer);ready()}},100)}window.onload=function(){ready()}};
, но проблема в том, если я хочу в коде две функции повесить на загрузку ДОМ. то первая соответственно уже не действует.

То решение что у вас просто замечательное, но если бы совместить с этим объектом, было бы бесценно!


Автор: Корниенко Виталий (не зарегистрирован), дата: 12 июня, 2009 - 22:35
#permalink

Соответственно на обработчик поставить так:
new DOMReady(function(){ my_func_start_onceDOMloaded(); });

new DOMReady(function(){ My_Func2_start_onceDOMloaded(); });

И Соответсвенно первая не должна запуститься!


Автор: Hank (не зарегистрирован), дата: 16 июня, 2020 - 22:02
#permalink

yes, I agree. thanks! fence company shreveport


Автор: Гость (не зарегистрирован), дата: 16 июня, 2020 - 22:32
#permalink

thanks this is great, thanks! kitchen remodeling spokane


Автор: hoid, дата: 13 августа, 2009 - 15:29
#permalink

на body и на elm(внутри body) были повешены события dblclick
в ie элементы-источники и соответственно обработчики понимались правильно,
а в остальных браузерах было, так будто бы на elm и не было события
я заменил

if(ret===false)

на

if(!ret)

заработало


Автор: Гость (не зарегистрирован), дата: 14 октября, 2009 - 11:07
#permalink

В ф-ии remove в конце

delete elem.handle
  delete elem.events 
}

не работает в IE (6.x)...
поставил вместо этого (на форуме подсказали)

elem['handle'] = undefined
  elem.events = undefined
}

тогда заработало...

Насколько я понял, в IE свойства элемента не удаляются, для объектов или массивов - без проблем, а вот для элементов - нет.
Ошибка?
Или как-то не так понимаю я?


Автор: Илья Кантор, дата: 14 октября, 2009 - 15:09
#permalink

Да, все верно, обновил статью. Там немного другой фикс.


Автор: Гость (не зарегистрирован), дата: 15 октября, 2009 - 08:19
#permalink

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


Автор: oid (не зарегистрирован), дата: 16 октября, 2009 - 17:35
#permalink

Хм, может стоит сделать определение типа браузера один раз, при создании объекта Event, чтобы этого не происходило при каждом вызове методов объекта?


Автор: vk65535, дата: 4 февраля, 2010 - 14:27
#permalink

Все это здорово, но очищать утечки памяти при unload-е не годится для долгоиграющих веб-приложений. К примеру, в ext-е эта проблема решается путем периодической проверки мусорного хэша элементов, и удаления тех, которые не в дом-дереве. Это тоже не очень-то хорошо, поскольку всякие всплывающие и выпадающие вещи обычно до поры лежат вне дом-дерева. Я эту проблему решил, отказавшись от ссылок на яваскрипт-объекты из обработчиков событий.


Автор: ROG, дата: 5 февраля, 2010 - 17:55
#permalink

в ie7 ругается на

for (var any in elem.events) return
	  try {
	    delete elem.handle
	    delete elem.events
	  } catch(e) { // IE
	    elem.removeAttribute("handle")
	    elem.removeAttribute("events")
	  }
    }

при удалении события

Event.remove(document, 'mousemove', MouseMoveEv);

Автор: Юриус (не зарегистрирован), дата: 14 апреля, 2010 - 22:16
#permalink

Можете рассказать, как в ie создавать свои собственные события и их обработчики? Firefox, Safari и Chrome это умеют. Что делать в ие?


Автор: Вовка Электроник (не зарегистрирован), дата: 11 мая, 2010 - 09:46
#permalink

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

if (evt.layerX == null)
{
evt.layerX = evt.x;
evt.layerY = evt.y;
}


Автор: all87 (не зарегистрирован), дата: 25 июня, 2010 - 02:53
#permalink

прежде всего Спасибо за Ваш ресурс, очень много информации с минимум воды и максимумом примеров!

Хотелось бы дополнить, возможно это исключается в части "Грамотная кросс-браузерная установка обработчиков", особо не вникал, но считаю следует дополнить "Первая попытка: attachEvent + addEventListener" еще одной неграмотностью IE - не игнорируется повторная регистрация существующего обработчика:

function test () { 	alert(1); }
	
	addEvent(document, 'click', test);
	addEvent(document, 'click', test);
	addEvent(document, 'click', test);

В IE 3 алерта.


Автор: Гость (не зарегистрирован), дата: 9 сентября, 2010 - 12:28
#permalink

Я наверное делаю что-то не так, но выражение typeof Event в IE<8 всегда дает undefined


Автор: Александр4444 (не зарегистрирован), дата: 26 октября, 2010 - 03:29
#permalink

Опять эти крези хукс!

(function(){...}())

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


Автор: Евгений Форманенко, дата: 8 января, 2011 - 18:39
#permalink

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

if (!handler) {
    for ( var handle in handlers ) {
    	delete events[type][handle];
    }
    return;
}

FIX

if (!handler) {
    for ( var handle in handlers ) {
        delete handlers[handle];
    }
    return;
}

Автор: Евгений Форманенко, дата: 8 января, 2011 - 18:40
#permalink

Исправив нашел баг - если не указать обработчика при удалении removeEventListener/detachEvent не удаляется. а значит commonHandle для эелемента все равно будет вызываться

FIX

function remove (elem, type, handler) {
	var handlers = elem.events && elem.events[type]
		    
	if (!handlers) return
		      
	if (handler) {
		delete handlers[handler.guid]
		for(var any in handlers) return 
	}
      
	if (elem.removeEventListener)
		elem.removeEventListener(type, elem.handle, false)
		else if (elem.detachEvent)
			elem.detachEvent("on" + type, elem.handle)
				
		delete elem.events[type];
			
			  
		for (var any in elem.events) return
			try {
				delete elem.handle
				delete elem.events 
		} catch(e) { // IE
			elem.removeAttribute("handle")
			elem.removeAttribute("events")
		}
}

Автор: Репозиторий суппозиториев (не зарегистрирован), дата: 26 февраля, 2011 - 14:11
#permalink

Пишете достаточно понятно, но не очень учитывая вероятный опыт читающих. Этим можно повергнуть в ступор. Например:
"Мини-библиотечка Event будет представлять собой синглтон..."
Этот "синглтон" у не "наблатыканного" в шаблонах ООП разработчика интерфейсов взорвет мозг. Не каждый разработчик интерфейсов слез с объектно-ориентированных Java, C++ или C# - возможно, парень верстал странички и решил освоит JS, и тут в него полетели синглтоны. Первый раз видя такое слово посетит мысль "И причем тут музыка?"


Автор: poorking, дата: 27 февраля, 2011 - 03:35
#permalink

14-я строка главы итоговый код:
event.stopPropagation = event.stopPropagaton
Это не опечатка?


Автор: Maxman, дата: 5 марта, 2011 - 18:49
#permalink

С учётом этой статьи, попробовал реализовать свою версию Event.js
Просьба оценить:
http://javascript.ru/forum/project/15587-moya-biblioteka-obrabotki-sobyt...


Автор: Гость (не зарегистрирован), дата: 15 апреля, 2011 - 21:27
#permalink

Вопрос! В какую часть кода добавляется новый обработчик? В конце или в функции add?


Автор: Гость (не зарегистрирован), дата: 21 апреля, 2011 - 09:28
#permalink

Нет ну правда

(function(){...}())

Ну ни где ранее я не увидел где описывается эта структура? Что это такое.. Смотрю я на Итоговый код и что вижу? Переменной присваивается какая-то структура, скобка, в ней безымянная функция, это типа конструктор что ли? Далее еще одна функция уже с именем и входным параметром. Далее оператор Return, мне напоминает оператор выбора case в других языках, все что в ретурне в скобках - {} и далее еще просто пустые скобки () - ну хоть поясните что это вообще такое? Спасибо...


Автор: Гость (не зарегистрирован), дата: 21 апреля, 2011 - 13:56
#permalink

Алилуя, сам разобрался!
И так, я бы на месте автора статьи чуток разжевал бы этот код. Во первых я не понимаю какой смысл в скобке перед первым function и в самом конце. И без нее все прекрасно. И так..

Event = function(){...}()

т.к. в конце указанно () - значит функция будет выполнена, и переменной Event присвоется не сама функция(ну или ее адрес, че там по факту), а результат ее выполнения - объект.

return {
    add: function(...){
      ...
    },
    remove: function(...){
      ...
    }
  }

элементы этого объекта 2 функции.

Казалось бы все очевидно, но, глядя на такое изобилие скобок, по началу путаешься) Кстати, тут как раз во всю используется замыкания, верно?


Автор: B@rmaley.e><e, дата: 21 апреля, 2011 - 17:46
#permalink

Скобки перед function нужны для читабельности кода. Они очень часто используются, когда нужно именно вызывать функцию, и такое использование служит "шаблоном", сообщающим, что будет присвоена не сама функция, а результат ее выполнения.
Конечно, это не обязательно так, но в рамках хорошего тона следует так делать.


Автор: brownsunny (не зарегистрирован), дата: 2 января, 2024 - 10:04
#permalink

suika game is a game for both adults and children, a game that stimulates strategic thinking. At the same time, the game is also designed very fresh with colorful tropical fruits.
Thanks


Автор: Dante_SSS (не зарегистрирован), дата: 21 мая, 2011 - 11:08
#permalink

Подскажите пожалуйста, как правильно передавать параметры в функцию (касается кросс-браузерного способа тут описанного)

var div1 = document.getElementById('div1');
function fun(text){
    alert(text);
}
Event.add(div1, 'click', fun('Hello'));

alert вылетает сразу при загрузке страницы, не дожидаясь события.

Собственно, этож же вопрос можно отнести к элементарному способу.

function doSomething(text){
    alert(text);
}
var div1 = document.getElementById('div1');
div1.onclick = doSomething('hello!');

Автор: Гость (не зарегистрирован), дата: 5 марта, 2018 - 11:52
#permalink

Необходимо передать указатель на функцию. Ты передаёшь результат её вызова. Заверни в function(event){...}.


Автор: Гость (не зарегистрирован), дата: 7 октября, 2011 - 03:37
#permalink

Dante_SSS спрашивал как передавать при добавлении динамического события передавать функцию с параметрами, никто чтоли не вкурсе?


Автор: Гость (не зарегистрирован), дата: 7 октября, 2011 - 05:38
#permalink

хм..

addEvent(elem, "click", function(e) { alert(a); toggle(document.getElementById('content'+a))} );

elem главное нормально в цикле отдает, а вот функцию почему-то content всегда последняя , 'а' счетчик; если же поставить статически 'content1', то работает правильно


Автор: Гость (не зарегистрирован), дата: 8 октября, 2011 - 01:53
#permalink

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


Автор: Гость (не зарегистрирован), дата: 24 октября, 2011 - 18:40
#permalink

1


Автор: Гость (не зарегистрирован), дата: 28 декабря, 2011 - 23:53
#permalink

хм...
Если мне допустим надо установить событие в другом событии, например так:

Event.addEvent(el,'mousedown',function(event){
	
		Event.addEvent(window,'mousemove', function(param1,param2){...});
	
	});
	
	Event.addEvent(window,'mouseup',function(){
	
	    Event.removeEvent(window,'mousemove', ??????? );
		
	});

При этом , мне надо передавать параметры в функцию в 3 строке.

Как мне потом удалить событие созданное в 3 стоке ?

Было бы неплохо , если бы addEvent возвращал номер события и добавить метод , который удалял бы событие по этому номеру.


Автор: restsent (не зарегистрирован), дата: 27 июля, 2023 - 10:45
#permalink

It's a pretty well-written piece of writing. Your site radiates your joy and optimistic outlook on life. I just wanted to say slope how much I benefited from reading your work. I'm going to put in the same amount of effort as you.


Автор: Andth (не зарегистрирован), дата: 11 мая, 2012 - 22:31
#permalink

Сам придумал. Вешаю один, как я его называю ГЛАВНЫЙ, обработчик событий на BODY (допустим onclick). Элементу, которому фактически нужен обработчик назначаю свойство data-click="myFunc" (проходит валидацию). Далее просто:

Главный обработчик

event = event || window.event;
var el = event.target || event.srcElement; // el - элемент на который фактически 
кликнули
var els=el.getAttribute("data-click");
if(els!==null)eval (els+"(el,e)");

В myFunc корректно передаются элемент el и событие event.
В Хроме и Опере последних работает, остальные не проверял.
С цепочкой обработчиков не заморачивался.
Плюсы: один обработчик на все события, можно делать с DOMom что угодно.
Минусы, пока не заметил.


Автор: seyfer, дата: 16 ноября, 2012 - 08:54
#permalink

Eval тут не хорошо.


Автор: Гость, дата: 1 июня, 2012 - 11:00
#permalink

Спасибо за статью. Долго мучался с IE6, но всеж победил благодаря этой статье))


Автор: Гость (не зарегистрирован), дата: 10 декабря, 2012 - 14:38
#permalink

подскажите пожалуйста!
Загружаю этот скрипт из другого методом document.createElement("script") в head.
1)Где угодно пытаюсь сделать Event.add - пишет - add is not a function.
Если прямо в файле библиотечки внизу строчку пишу Event.add(document,'click',function(){alert('1')}) - тогда норм.
2)Если пытаюсь повешать что-нибудь на событие DOMContentLoaded или load - то не срабатывает. Подскажите что почитать или посоветуйте (желательно ссылкой или точным названием темы). Ну и по этому случаю если подскажете, то будет отлично!


Автор: Гость (не зарегистрирован), дата: 4 февраля, 2013 - 09:06
#permalink

Объясните пожалуйста строки: исправляем небольшой глюк IE с передачей объекта window
if (elem.setInterval && ( elem != window && !elem.frameElement ) ) {
elem = window;
}

1) что за глюк? когда возникает? актуальна ли проблема, если не поддерживать ie6 и нет frame
2) я так понимаю скобки в условии if для красоты


Автор: vy4eslavik, дата: 3 апреля, 2013 - 14:28
#permalink

в Opera событие load на картинку всё-равно не работает!


Автор: voltag (не зарегистрирован), дата: 25 ноября, 2013 - 18:39
#permalink

Уважаемый автор. У меня возникла проблема, которую я решил...
Event.add(elem, 'onclick', function() { alert('hi');} );
бился я долго над onclick - я лошара, я согласен...
я понимаю что click - писать короче. Но есть стандартные события.
Если бы чудесный класс, ещё распознавал стандартное написание, было бы замечательно - ИМХО так просто привычнее.

Спасибо Автору за труд


Автор: Hapson, дата: 4 января, 2014 - 01:03
#permalink

// исправляем небольшой глюк IE с передачей объекта window

Скажите, что за глюк такой?
Я понимаю, что придет время и столкнусь с ним, тогда и узнаю... если столкнусь.
Но хочется заранее узнать, о чем речь идет.


Автор: Hapson, дата: 4 января, 2014 - 01:54
#permalink

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

remove: function(elem, type, handler){
	var handlers = elem.events && elem.events[type];
	if(!handlers){return;}
	var searchHandler = false;
	for(z in elem.events[type]){
		if(elem.events[type][z] == handler){searchHandler = true;}
	}
	if(!searchHandler){return;}
	delete handlers[handler.guid];

К примеру: я добавил обработчик клика на диве. Функция обработчик сразу снимает обработчик с события и потом уже выполняет что надо. Если возникнет ошибка при снятии обработчика, то функция не отработает.


Автор: Hapson, дата: 4 января, 2014 - 02:28
#permalink

Или так

remove: function(elem, type, handler){
	var handlers = elem.events && elem.events[type];
	if(!handlers || (handlers[handler.guid] != handler)){return;}
	delete handlers[handler.guid];

Автор: Гость (не зарегистрирован), дата: 31 марта, 2014 - 19:21
#permalink

Обработчику присваивается уникальный порядковый номер. Если впоследствии использовать обработчик для другого контрола, то наверняка его номер буден меньше, чем другие новые обработчики: даже если добавить обработчик последним, он вывполняться будет первым (согласно своего уникального номера)!


Автор: мистраль (не зарегистрирован), дата: 7 мая, 2014 - 00:10
#permalink

Понимаю, что тема давняя и не совсем об этом, но ОЧЕНЬ прошу разъяснить почему нельзя обрабатывать события, например, так:

<div onmouseover="func();">Content</div>

Автор: Гость (не зарегистрирован), дата: 28 октября, 2014 - 13:42
#permalink

Корректно ли с т.з. кроссбраузерности удалять событие изнутри функции через arguments.callee?

Например,

var el = document.getElementById('test');
var f = function()
	{
	Event.remove(this, 'click', arguments.callee);
	alert('Test!');
	}
Event.add(el, 'click', f);

Автор: wondertalik (не зарегистрирован), дата: 2 декабря, 2014 - 15:12
#permalink
remove: function(elem, type, handler) {
    var handlers = elem.events && elem.events[type]
      
    if (!handlers) return

    if (!handler) {
        for ( var handle in handlers ) {
            delete events[type][handle]
        }
        return
    }

    // остальная часть функции - без изменений 
    delete handlers[handler.guid]
    // ...
}
delete events[type][handle]

опечатка?

delete elem.events[type][handle]

Поправьте пожалуйста.


Автор: Гость (не зарегистрирован), дата: 26 февраля, 2016 - 22:56
#permalink

return {
add: function(elem, type, handler) {
// добавить обработчик события
},
remove: function(elem, type, handler) {
// удалить обработчик события
}
=============================

this.add = function(elem, type, handler) {

}

this.remove = function(elem, type, handler) {

}

Подскажите пожалуйста, в чем разница?


Автор: Гость (не зарегистрирован), дата: 13 июня, 2018 - 20:38
#permalink

Пригодится в разработке любых программ и сайтов.


Автор: Jim Harxmon (не зарегистрирован), дата: 23 декабря, 2020 - 07:26
#permalink

Javascript is one of the most popular programming language and it is mostly use in Web development, thank you for discussing those function and other syntax of Javascript. download bed wars on your PC and mobile and see for yourself why this game is popular right now. You can watch streamers to learn a tips and tricks on the game


Автор: Ahmed Sayeed (не зарегистрирован), дата: 21 марта, 2021 - 13:22
#permalink

Автор: Гость (не зарегистрирован), дата: 31 марта, 2022 - 10:28
#permalink

Please allow me to share your article. It's really practical and meaningful paper minecraft


Автор: Гость (не зарегистрирован), дата: 12 апреля, 2022 - 17:42
#permalink

Автор: Flora Toledano Talamantes (не зарегистрирован), дата: 12 апреля, 2022 - 20:36
#permalink

Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 03:08
#permalink

Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 11:59
#permalink

Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 14:42
#permalink

Автор: Yvonne Mouret (не зарегистрирован), дата: 20 апреля, 2022 - 20:38
#permalink

Sexe intime est l'application de rencontre que je recommande. Si vous savez peu de choses sur une personne, établir un premier contact peut être un grand défi. Vous devez passer au crible une multitude de profils, ce qui fait qu'il est facile de laisser passer des personnes auxquelles vous auriez peut-être donné une chance dans d'autres circonstances.


Автор: Maren Hartmann (не зарегистрирован), дата: 25 мая, 2022 - 12:08
#permalink

Sex Bremen ist stolz auf sein Hauptmerkmal: den Sex-Chat, denn er ist kostenlos und ohne jegliche Einschränkungen. Lesen Sie weiter und erfahren Sie mehr über diese beliebte Chat-Plattform und finden Sie heraus, wie Sie mitmachen und Ihr Leben spannender und lustiger gestalten können.


Автор: patrick rall (не зарегистрирован), дата: 15 июня, 2022 - 19:57
#permalink

Informative and resourceful sites such as this one are rare to find. Thank you very much for sharing this.

https://lamundial.net/literas-para-adultos/


Автор: moranmc, дата: 1 июля, 2022 - 15:43
#permalink

Do you like playing online games in your free time? If it is your favorite activity, I recommend to you guys an interesting game named diggy. You can play this game in your free time. Let's try and enjoy it.


Автор: mytom2, дата: 12 июля, 2022 - 18:06
#permalink

fnf games are a form of interactive entertainment that is based on a fictional world. They have rules, systems, and storylines that are different from traditional video games.

The popularity of fnf games has increased over the past few years. They have been especially popular among millennials and young adults who often prefer to play these types of games instead of traditional video games.

A lot of studies show that fnf game players tend to be more creative than their counterparts who play traditional video games.

You can participate in this game at our kbh games website!


Автор: Гость (не зарегистрирован), дата: 13 августа, 2022 - 04:16
#permalink

Well hello comrades. I don't know if anyone here knows the Penalty Shooters 2 yet? I'm immersed in it and really want to have more teammates to conquer higher levels together.


Автор: sarahroxon (не зарегистрирован), дата: 11 октября, 2022 - 10:48
#permalink

I'm going to share with you an entertaining slope game with you. This is a popular gaming website. A fun online game that has simple controls and excellent sound. You can go to this page if you are more interested in specific game subjects.


Автор: Гость (не зарегистрирован), дата: 11 октября, 2022 - 12:21
#permalink

What you share has a lot of useful information, very useful to me, and good for the community in general. Please let us know what's going on. special thanks! LOLbeans io


Автор: Гость (не зарегистрирован), дата: 11 октября, 2022 - 12:21
#permalink

What you share has a lot of useful information, very useful to me, and good for the community in general. Please let us know what's going on. special thanks! LOLbeans io


Автор: amityusa, дата: 15 ноября, 2022 - 07:44
#permalink

I really like this blog, it shares a lot of information that I want to know in many areas. I hope there will be more interesting things tetris unblocked in the future.


Автор: IrinaBer90 (не зарегистрирован), дата: 7 января, 2023 - 17:22
#permalink

It's up to people whether they choose to use positive words that start with S or not. We all have free will.


Автор: Гость (не зарегистрирован), дата: 31 января, 2023 - 06:13
#permalink

More than 10,000 Unblocked Gamez are available. One of the top 66 online unblocked gamez every day! We have the most recent games available.


Автор: Bekean (не зарегистрирован), дата: 13 марта, 2023 - 07:06
#permalink

Be aware of the different obstacles in the subway surfers online game, such as trains, barriers, and gaps in the road. Jump, slide, and dodge your way past them to avoid losing the game.


Автор: Marcus Berry (не зарегистрирован), дата: 24 марта, 2023 - 04:58
#permalink

Great run. Love to see this speedrun more vampire survivors


Автор: nokida (не зарегистрирован), дата: 13 апреля, 2023 - 17:36
#permalink

All of the information I’ve gleaned from it basically has been quite useful fnaf
, and I’d for the most part want to generally commend you on really your abilities, actually contrary to popular belief. five nights at freddy's


Автор: Гость (не зарегистрирован), дата: 5 июня, 2023 - 05:00
#permalink

I am so happy to come across this piece of write up, very much advanced my understanding to the next top level. Great job and continue to do same. lol shot io


Автор: Yukisoa (не зарегистрирован), дата: 7 июня, 2023 - 12:36
#permalink

There are many ways to have fun, I often play some online games like amanda the adventurer to solve interesting puzzles, it is a horror game and you will be faced with spooky things.


Автор: adamusa (не зарегистрирован), дата: 8 июня, 2023 - 10:47
#permalink

I believe that with the information you share, it will bring a lot of value to the readers and I hope that they will absorb the good and useful things. route planner


Автор: LOLBEANS (не зарегистрирован), дата: 27 июня, 2023 - 05:59
#permalink

I am constantly amazed by the amount of information available on the subject run 3. What you present has been well researched and well worded to be able to get your point of view on this matter to all your readers.


Автор: sckefb (не зарегистрирован), дата: 6 июля, 2023 - 09:07
#permalink

CNC machining is a general term used for a variety of machining applications. “CNC” stands for Computer Numerical Controlled and refers to the programmable feature of the machine, allowing the machine to perform many functions with minimal human control. cnc machined parts


Автор: Eve (не зарегистрирован), дата: 6 июля, 2023 - 10:31
#permalink

The handler function, handle, will be executed by the browser whenever the event takes place papa's pizzeria. And already, she is aware of her element, thus she is able to receive an object event and launch the handlers that have been assigned from the list.


Автор: Paul Hurley (не зарегистрирован), дата: 19 июля, 2023 - 14:05
#permalink

Thanks for letting me link to your article. It's significant and useful in so many ways. connections game


Автор: elijahnelson (не зарегистрирован), дата: 26 июля, 2023 - 09:50
#permalink

This implementation is concise, understandable, and works across different browsers, including IE6+, Firefox, Opera, driving directions and Safari.


Автор: Hosen (не зарегистрирован), дата: 7 августа, 2023 - 23:08
#permalink

Some people share stupid tests on FB and it is so annoying, is there any way to block only posts which are about different tests? Play play mahjongg solitaire aarp


Автор: Losudia (не зарегистрирован), дата: 12 сентября, 2023 - 06:48
#permalink

I appreciate your posting mapquest driving directions. I've read about a lot of related subjects!


Автор: Гость (не зарегистрирован), дата: 14 сентября, 2023 - 11:23
#permalink

I appreciate you allowing me to link to your article. It serves a variety of purposes and is significant. cookie clicker


Автор: samguerra (не зарегистрирован), дата: 14 сентября, 2023 - 15:16
#permalink

I hate hypocrisy, the only thing that really pisses me off. When people are doing it, it really looks ugly.


Автор: samguerra (не зарегистрирован), дата: 14 сентября, 2023 - 15:17
#permalink

Basic education is so important, yet our education system completely ignores it.


Автор: Bipil (не зарегистрирован), дата: 12 октября, 2023 - 02:31
#permalink

Radio controlled toys were always something special for me.
https://blobopera.org


Автор: hadleyquintanilla (не зарегистрирован), дата: 31 октября, 2023 - 14:46
#permalink

Bunch of noobs, everyone picked carry in unblocked vat ninja, we had no wards and they were wondering why we were losing… pathetic… aarp bubble shooter game


Автор: hadleyquintanilla (не зарегистрирован), дата: 31 октября, 2023 - 14:48
#permalink

What kind of keyboards do you love guys in 1v1 LoL? I love wired ones and with LED light so I can see keys in dark play tetr io.


Автор: antonettepinkston (не зарегистрирован), дата: 25 декабря, 2023 - 16:16
#permalink

I have such a wonderful memories with old console games… Games like Mario, Tanks, Looney Tunes, etc.


Автор: antonettepinkston (не зарегистрирован), дата: 25 декабря, 2023 - 16:17
#permalink

You are so naïve, you really think that they will let you to have it your way?


Автор: superedan (не зарегистрирован), дата: 9 марта, 2024 - 05:38
#permalink

The inclusion of relevant statistics and data enhances the credibility of the article, reinforcing the points made Watermelon Game and adding a layer of factual support.


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
1 + 2 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum