18.07.2011, 05:36
|
|
Новичок на форуме
|
|
Регистрация: 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 = ...", в результате возникают перечисленные выше глюки.
Код для "меню" лежит здесь...
|
|
18.07.2011, 06:39
|
|
Быдлокодер;)
|
|
Регистрация: 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, 18.07.2011 в 07:11.
|
|
18.07.2011, 07:27
|
|
Новичок на форуме
|
|
Регистрация: 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 лучше вообще не трогать, ибо при работе с ним мы всегда сначала создаём его копию, а затем совершаем присвоение со всеми неизбежными неприятностями.
|
|
18.07.2011, 09:04
|
|
Модератор
|
|
Регистрация: 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.
Последний раз редактировалось trikadin, 18.07.2011 в 09:07.
|
|
18.07.2011, 11:48
|
Новичок на форуме
|
|
Регистрация: 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); // мы его может повторно в дерево засунуть.
|
|
18.07.2011, 14:41
|
|
Быдлокодер;)
|
|
Регистрация: 19.11.2010
Сообщений: 4,338
|
|
Цитата:
|
"Мёртвая" ссылка должна быть именно мёртвой после сброса всего содержимого body. Однако в реальности приходится сталкиваться с тем, что после перезагрузки страницы сохранилась не только сама переменная ptrID, но и тот объект, на который она ссылалась до перезагрузки.Так быть не должно, ИМХО. Это крайне нелогично.
|
Сборщик мусора не грохнет эту ссылку, пока с ней связанна хотя бы одна переменная - т.е. обратите ваши переменные в null и всё потрётся окончательно. А так, всё именно так, как сказал Kolyaj.
Цитата:
|
С onLoad тоже логики не вижу. Если я сбросил всё содержимое body, как Вы выразились, и браузер фактически загрузил снова все объекты по второму разу (что отражается на счетчике, к примеру), то и событие onLoad он должен был бы вызвать повторно (а обработчик его ведь так и висит на body, поскольку изменен был innerHTML, а не outerHTML).
|
Событие onLoad связанно со всем документом (объект window, и работает так, как сказал trikadin), а вы сбрасываете лишь часть DOMа (фактически сбрасываете свойства и делаете перерендер). Можно эмулировать событие DOM content ready, но имхо нафик не нужно это всё.
Последний раз редактировалось kobezzza, 18.07.2011 в 14:58.
|
|
19.07.2011, 01:41
|
|
Новичок на форуме
|
|
Регистрация: 18.07.2011
Сообщений: 3
|
|
Ох-ох-ох))))) Да, уж, к этому нужно привыкнуть...
Спасибо всем вам за весьма толковые разъяснения!
|
|
|
|