Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 18.07.2011, 04:36
Аватар для andy_quarry
Новичок на форуме
Отправить личное сообщение для andy_quarry Посмотреть профиль Найти все сообщения от andy_quarry
 
Регистрация: 18.07.2011
Сообщений: 3

body.innerhtml - что за чертовщина?
Первый раз пишу на форум - может не в тот раздел... но решил вот, что сюда мой вопрос по-любому подходит. Искал-искал на этом сайте и в инете, но не попалось ничего по проблеме, с которой столкнулся.

Кто может толково разъяснить, что творится со страницей после присвоение дополнительной строки к body.innerhtml? У меня впечатление, что в ДОМе всё слетает нафик после такой казалось бы безобидной операции.

Ну, т.е. отдельный скрипт добавляет в документ новый div со всем его содержимым, используя для этого body.innerhtml:
body.innerhtml = '<div>что-то там</div>' + body.innerhtml;

И после этого на странице начинаются чудеса.

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

Заметил следующие глюки:
1. Слетают все указатели, полученные прежде по getElementById. То есть происходит в реале ещё более странно: прежние объекты, на которые получен указатель, продолжают существовать и жить своей жизнью, но на экране они не видны, а вместо них появляются их двойники с теми же ИД.

2. Нарушается позиционирование элементов на странице. Т.е., например, top отсчитывается словно не от нуля, а со положительным смещением.

3. Да и вообще страница словно заново перегружается (счетчики дважды обновляются). Я это ещё мог бы понять, если бы вообще все объекты, созданные после первой загрузки, напрочь удалялись при повторной загрузке. Однако это не так - см. п.1.

4. Ну, и соответственно, обработчик onLoad глючит конкретно: выполнение его прерывается после выполнения строки "body.innerhtml = ...", но после повторной загрузки страницы он всё же второй раз не вызывается.

При таких глюках было бы правильнее, КМК, вообще изъять innerhtml у body. С другими тэгами таких глюков не заметил.

Чтобы проиллюстрировать эти проблемы, могу предложить сравнить нормальную работу скрипта, фиксирующего положение <div> и обеспечивающего прокрутку страницы, и его нарушенную работу при его взаимодействии со скриптом, добавляющим в документ новый div вышеуказанным способом.

"Нормальный" скрипт лежит тут... Он содержит два класса: автоматический прокрутчик страницы и фиксатор div-ов.

Пример нормальной работы кода обоих классов можно увидеть на главной странице моего сайта... Нажатие любой клавиши включает/выключает автопрокрутку. А после прокрутки на пол-окна плавно появляется справа ссылка "Наверх". Или вот тут пример автопрокрутки при иных параметрах. А здесь пример одновременной фиксации двух div-вов (навешиваются списком к классу "фиксатор").

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

То есть, после того, как к странице присоединяется меню при помощи "body.innerhtml = ...", в результате возникают перечисленные выше глюки.

Код для "меню" лежит здесь...
Ответить с цитированием
  #2 (permalink)  
Старый 18.07.2011, 05:39
Аватар для kobezzza
Быдлокодер;)
Отправить личное сообщение для kobezzza Посмотреть профиль Найти все сообщения от kobezzza
 
Регистрация: 19.11.2010
Сообщений: 4,338

Цитата:
body.innerhtml = '<div>что-то там</div>' + body.innerhtml;
Свойство пишется так: innerHTML

Цитата:
1. Слетают все указатели, полученные прежде по getElementById. То есть происходит в реале ещё более странно: прежние объекты, на которые получен указатель, продолжают существовать и жить своей жизнью, но на экране они не видны, а вместо них появляются их двойники с теми же ИД.
Ну сам getElementById - это "мёртвая" ссылка, т.е. она указывает на определённый элемент и в случае его замены/удаления ссылка не обновляется (в отличии от getElements методов), а смена свойства innerHTML по сути является заменой, так что тут всё логично. А не удаляются эти указатели по той-же причине - они не привязаны к DOM напрямую.

Допустим у нас есть такой HTML код:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Пример</title>
</head>

<body>
<h2 id="h2">Заголовок</h2>

</body>
</html>


И мы через JS делаем:
var h2 = document.getElementById("h2"),
h2byName = document.getElementsByTagName("h2"),
myBody = document.getElementsByTagName("body")[0];

myBody.innerHTML = myBody.innerHTML;
h2.innerHTML = "Пыщ Пыщ!"; // Упс! Кажется бага
h2byName[0].innerHTML = "Пыщ Пыщ!"; // Ура, всё ок!


Цитата:
2. Нарушается позиционирование элементов на странице. Т.е., например, top отсчитывается словно не от нуля, а со положительным смещением.
Тут я честно хз почему так)

Цитата:
3. Да и вообще страница словно заново перегружается (счетчики дважды обновляются). Я это ещё мог бы понять, если бы вообще все объекты, созданные после первой загрузки, напрочь удалялись при повторной загрузке. Однако это не так - см. п.1.
По сути - это и происходит. Почему не удаляются ссылки я писал выше.

Цитата:
4. Ну, и соответственно, обработчик onLoad глючит конкретно: выполнение его прерывается после выполнения строки "body.innerhtml = ...", но после повторной загрузки страницы он всё же второй раз не вызывается.
Тут тоже всё логично, т.к. вы сбросили всё содержимое body.

Цитата:
При таких глюках было бы правильнее, КМК, вообще изъять innerhtml у body. С другими тэгами таких глюков не заметил.
Вам повезло, innerHTML в разных браузерах может багать, например помнится в ИЕ были проблемы с тегом style и вставкой ячеек таблицы.

Вообще свойство innerHTML может часто вызывать недопонимание, например если вставить тег script через innerHTML, то он не будет работать)

Я думаю, что при вставки кода таким способом браузер рендерит строку, но не выделяет память на всякие "физические" операции, типо там выполнение скрипта или подгрузка стилей и т.д.
__________________
kobezzza
code monkey

Последний раз редактировалось kobezzza, 18.07.2011 в 06:11.
Ответить с цитированием
  #3 (permalink)  
Старый 18.07.2011, 06:27
Аватар для andy_quarry
Новичок на форуме
Отправить личное сообщение для andy_quarry Посмотреть профиль Найти все сообщения от andy_quarry
 
Регистрация: 18.07.2011
Сообщений: 3

Ну, по нескольким пунктам никак не могу тут согласиться с "логичностью".

Сообщение от kobezzza Посмотреть сообщение
Ну сам getElementById - это "мёртвая" ссылка
Вот же ж я этого и ожидал... То есть что мы имеем? Моя переменная ptrID хранит ссылку на объект с каким-то ID. Теперь, после той проделки с body.innerHTML прежний объект с тем ID разрушен и создан новый объект с тем же ID и с новым адресом. Следовательно, моя переменная ptrID (раз уж она после перезагрузки сохранилась, что странно уже само по себе) указывает на несуществующий объект - и, следовательно, при работе с ней (например, при выполнении кода ptrID.innerHTML = 'что-то там') должна вызвать ошибку нарушения памяти или нечто такое.

"Мёртвая" ссылка должна быть именно мёртвой после сброса всего содержимого body. Однако в реальности приходится сталкиваться с тем, что после перезагрузки страницы сохранилась не только сама переменная ptrID, но и тот объект, на который она ссылалась до перезагрузки. Так быть не должно, ИМХО. Это крайне нелогично.


С onLoad тоже логики не вижу. Если я сбросил всё содержимое body, как Вы выразились, и браузер фактически загрузил снова все объекты по второму разу (что отражается на счетчике, к примеру), то и событие onLoad он должен был бы вызвать повторно (а обработчик его ведь так и висит на body, поскольку изменен был innerHTML, а не outerHTML).

Ну, т.е. итог: вижу, что этого всего абсурда обойти не удастся кроме как цеплять div через createElement. И, похоже, что body.innerHTML лучше вообще не трогать, ибо при работе с ним мы всегда сначала создаём его копию, а затем совершаем присвоение со всеми неизбежными неприятностями.
Ответить с цитированием
  #4 (permalink)  
Старый 18.07.2011, 08:04
Аватар для trikadin
Модератор
Отправить личное сообщение для trikadin Посмотреть профиль Найти все сообщения от trikadin
 
Регистрация: 27.04.2010
Сообщений: 3,417

Сообщение от andy_quarry
С onLoad тоже логики не вижу. Если я сбросил всё содержимое body, как Вы выразились, и браузер фактически загрузил снова все объекты по второму разу (что отражается на счетчике, к примеру), то и событие onLoad он должен был бы вызвать повторно (а обработчик его ведь так и висит на body, поскольку изменен был innerHTML, а не outerHTML).
Есть логика. Да хотя бы в названии. Onload - событие, наступающее при полной загрузке (ну и рендере, да) документа с сервера. А тут перезагрузки с сервера нету, только перендер. С таким же успехом можно ожидать событие onload, если, скажем, удалить все элементы со страницы (с помощью removeElement), а потом добавить новый.
Сообщение от andy_quarry
"Мёртвая" ссылка должна быть именно мёртвой после сброса всего содержимого body. Однако в реальности приходится сталкиваться с тем, что после перезагрузки страницы сохранилась не только сама переменная ptrID, но и тот объект, на который она ссылалась до перезагрузки.Так быть не должно, ИМХО. Это крайне нелогично.
Вполне логично. Браузер не знает, будете ли вы пользоваться ещё тем объектом, на который получили ссылку через getElementById.
Сообщение от andy_quarry
Так быть не должно, ИМХО
Вам в w3c)
Сообщение от andy_quarry
Ну, т.е. итог: вижу, что этого всего абсурда обойти не удастся кроме как цеплять div через createElement. И, похоже, что body.innerHTML лучше вообще не трогать, ибо при работе с ним мы всегда сначала создаём его копию, а затем совершаем присвоение со всеми неизбежными неприятностями.
Правильный вывод. Плюс createElement (и appendChild/insertBefore дальше) работают быстрее, чем если создавать элементы через innerHTML.
__________________
Читайте:
Ты любопытный) Всё-таки, ничему в этом мире не помешает хорошая доля юмора)
Как спросить, чтобы вам ответили
Часто Задаваемые Вопросы (FAQ)

Последний раз редактировалось trikadin, 18.07.2011 в 08:07.
Ответить с цитированием
  #5 (permalink)  
Старый 18.07.2011, 10:48
Новичок на форуме
Отправить личное сообщение для Kolyaj Посмотреть профиль Найти все сообщения от Kolyaj
 
Регистрация: 19.02.2008
Сообщений: 9,177

Сообщение от andy_quarry
Теперь, после той проделки с body.innerHTML прежний объект с тем ID разрушен и создан новый объект с тем же ID и с новым адресом.
Не разрушен. Он точно так же существует, просто его удалили со страницы, он не отображается.
Например,
var el = document.createElement('div'); // Создали новый div, в дереве его нет, он не отображается, но он существует
el.style.color = 'red'; // можем менять его свойства
// можем добавлять ему детей, если надо
document.body.appendChild(div); // вот теперь мы его добавили в существующее дерево, теперь он отобразится.
document.body.removeChild(div); // а теперь удалили из дерева, он теперь не отображается, но по прежнему существует.
document.body.appendChild(div); // мы его может повторно в дерево засунуть.
Ответить с цитированием
  #6 (permalink)  
Старый 18.07.2011, 13:41
Аватар для kobezzza
Быдлокодер;)
Отправить личное сообщение для kobezzza Посмотреть профиль Найти все сообщения от kobezzza
 
Регистрация: 19.11.2010
Сообщений: 4,338

Цитата:
"Мёртвая" ссылка должна быть именно мёртвой после сброса всего содержимого body. Однако в реальности приходится сталкиваться с тем, что после перезагрузки страницы сохранилась не только сама переменная ptrID, но и тот объект, на который она ссылалась до перезагрузки.Так быть не должно, ИМХО. Это крайне нелогично.
Сборщик мусора не грохнет эту ссылку, пока с ней связанна хотя бы одна переменная - т.е. обратите ваши переменные в null и всё потрётся окончательно. А так, всё именно так, как сказал Kolyaj.

Цитата:
С onLoad тоже логики не вижу. Если я сбросил всё содержимое body, как Вы выразились, и браузер фактически загрузил снова все объекты по второму разу (что отражается на счетчике, к примеру), то и событие onLoad он должен был бы вызвать повторно (а обработчик его ведь так и висит на body, поскольку изменен был innerHTML, а не outerHTML).
Событие onLoad связанно со всем документом (объект window, и работает так, как сказал trikadin), а вы сбрасываете лишь часть DOMа (фактически сбрасываете свойства и делаете перерендер). Можно эмулировать событие DOM content ready, но имхо нафик не нужно это всё.
__________________
kobezzza
code monkey

Последний раз редактировалось kobezzza, 18.07.2011 в 13:58.
Ответить с цитированием
  #7 (permalink)  
Старый 19.07.2011, 00:41
Аватар для andy_quarry
Новичок на форуме
Отправить личное сообщение для andy_quarry Посмотреть профиль Найти все сообщения от andy_quarry
 
Регистрация: 18.07.2011
Сообщений: 3

Ох-ох-ох))))) Да, уж, к этому нужно привыкнуть...

Спасибо всем вам за весьма толковые разъяснения!
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
О фрилансе (Личный опыт) free Оффтопик 105 18.08.2011 16:02
Вполнение Javascript в XML B~Vladi (X)HTML/CSS 110 19.04.2011 18:46
Выполняется функция, а все что написано после нее игнорируется, libinstyle Элементы интерфейса 4 24.03.2010 15:44
Что это $(' бла-бла ') Виталл Общие вопросы Javascript 6 19.03.2010 14:04
Что за http.responseText;??? vanderv AJAX и COMET 5 02.11.2009 14:34