Этот сайт не наркоманов. Это сайт программистов.

Добро пожаловать на Пыху!

Логин:
Пароль:
 

Нет прописки? Зарегистрируйся!

Новости

Пыха информатор 3.1
Еще более удобное оповещение о флуде!

Краснодарское время: 23 Май, 2012, 01:26:23

Страниц: [1]
Печать
Автор Тема: Обработка документа в браузере  (Прочитано 5971 раз)
0 Пользователей и 1 Гость смотрят эту тему.
vasa_c    ↓ 
02 Сентябрь, 2007, 06:19:10
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Понятно, что здесь в основном php-шники, но все-таки.
Уровень: для самых маленьких.
 
Обработка документа в браузере

  • куда девались теги?

  • что у меня с кодировкой?

  • почему не добавляются html-сущности?

  • с document.write() постоянно какие-то траблы

  • почему не могу записать текст в элемент?

  • да почему у меня опять ничего не работает?!

Изложенные здесь вещи, на первый взгляд, элементарны. Однако большинство начинающих их не понимают, что приводит к серьезным осложнениям. Впрочем, многие уже, казалось бы, давно не начинающие, их тоже не понимают.
 
Основное, что нужно понять, это то, с чем работает браузер, когда показывает нам html-страницу. Так вот работает он не с тем самым html-файликом, который мы ему скормили и который содержит html-разметку. А работает он с деревом элементов. Деревом, которое получается путем разбора того самого html-файла.
 
Т.е. имеем:
html-файл на диске --> разбор файла при загрузке в браузер --> работа с получившимся деревом
 
Что такое (XML-)дерево, надеюсь, подробно рассказывать не нужно. Набор элементов (узлов), каждый из которых может иметь вложенные элементы. В корне дерева элемент «HTML», в него вложены «HEAD» и «BODY», в них вложено еще что-то. В самом конце иерархии либо пустые элементы (IMG), либо текстовые узлы. Каждый элемент так же может иметь набор атрибутов. Атрибут характеризуется именем (уникальным в рамках элемента) и значением (строкой). Вот и все. В смысле, еще есть комментарии, DOCTYPE и еще много чего, но это к делу не относится.
 
Основная разница между html-деревом и html-файлом: дерево, это структура с которой работает браузер, а файл, это описание этой структуры, удобное для хранения на диске и прочтения человеком.
 

Теги, кавычки, html-сущности
 
Описание структуры страницы (то самое, которое лежит в текстовом файле), должно соответствовать какому-то формату для того, чтобы разборщик мог понять, что мы хотели этим сказать и разобрать нашу писанину в дерево.
 
Правила эти просты и общеизвестны:

  1. Элементы задаются с помощью тегов (открывающего и закрывающего). Открывающий тег содержит имя элемента (и, возможно, описание его атрибутов), заключенное в знаки “<” и “>”. Закрывающий тег имеет формат </имя_тега>. Все, что между ними, считается содержимым элемента.
  2. Значения атрибутов, желательно, заключать в кавычки. Там же, где значения содержат пробелы, без кавычек не обойтись, так как разборщик просто не поймет, что это - все еще значение атрибута, или же новый атрибут.
  3. Как текст на странице, так и значения атрибутов могут иметь абсолютно любое значение, т.е. быть последовательностью абсолютно любых символов, абсолютно любой длины (в теории). Но так, как некоторые символы могут быть поняты разборщиком неправильно, вместо них следует ставить т.н. html-сущности, которые символизируют эти символы (< & и др)
И так далее, и тому подобное. Не буду пересказывать основы html.
 
Так вот, главное, что нужно понимать, это то, что все это имеет место быть, только для исходного html-кода. К дереву с которым работает браузер (и с которым может работать js-программист) оно не имеет отношения.
 
В разобранном html-документе нет никаких открывающих и закрывающих тегов и нет никаких “<” и “>”. Они нужны были только для описания структуры в тексте и теперь больше не нужны. DOM-свойство tagName возвращает имя элемента без символов больше-меньше. При создании элемента, через document.createElement() не нужно указывать “<>” (хотя иногда можно, а в некоторых случаях и полезно).
 
У атрибутов нет никаких кавычек. Они нужны были только для того, чтобы отделить значение от окружающего текста в исходном коде.
 
И нету никаких html-сущностей. Все они преобразованы в нужные символы на этапе разбора.
 
HTML
<div id="test">Текст &laquo;в кавычках&raquo;</div>
<script type="text/javascript">
    var dt = document.getElementById("test");
    alert(dt.firstChild.nodeValue);
    dt.firstChild.nodeValue += "еще &" + "laquo; кавычка";
</script>

alert() выводит кавычки в виде символов, а не сущностей. Динамическое добавление последовательности « к тексту не приводит к преобразованию ее в символ кавычки.
 
Некоторых смущает такое свойство DOM-элементов, как innerHTML, которое создает иллюзию привязки к элементу какого-то HTML-кода. На самом деле, это просто ловкость рук и никакого мошенничества. Когда программист читает из этого свойства, происходит вызов функции, которая формирует из нужной части дерева html-код. Когда записывает, этот код вновь разбирается в часть дерева, которое цепляется в нужном месте. Даже если присмотреться, то innerHTML возвращает не всегда дословно то, что было в исходном коде. Разные браузеры по разному могут менять регистр имен тегов, порядок атрибутов, кавычек и т.п.
 

Кодировка
 
Все современные браузеры используют в качестве внутренней кодировки UTF.
 
Кодировка документа, которая указывается в теге META либо в заголовке Content-Type, не указывает браузеру, в какой кодировке ему следует работать. А указывает разборщику, в какой кодировке пришел документ, с тем, чтобы он перекодировал его в UTF.
 
Например, создаем html-файл в котором задаем кодировку:
HTML
<meta http-equiv="content-type" content="text/html; charset=windows-1251" />
так же в нем подключаем сценарий в другой кодировке
HTML
<script type="text/javascript" src="./test.js" charset="koi8-r"></script>
а на странице делаем iframe в который загружаем другой документ, который использует кодировку ISO.
 
Что означают эти манипуляции? Они не означают то, что будут загружены два документа и один сценарий в трех разных кодировках и, в случае организации взаимодействия между ними, придется с помощью js производить перекодировку. Они означают то, что все три файла при загрузке первым делом будут перекодированы, каждый из своей кодировки, в UTF. И в итоге они будут работать и взаимодействовать в одной и той же кодировке.
 
Вопрос 1
 
У меня есть документ, который находится в кодировке windows-1251, в нем есть код:
Javascript
if (str.charCodeAt(0) == 224) alert("Yes!");
я точно знаю, что строка начинается с символа “а”, а его код в win1251 – 224. Но ничего не работает. Для проверки я сделал так:
Javascript
var str = "абвгд";
  if (str.charCodeAt(0) == 224) alert("Yes!");
и даже так:
Javascript
if ("абвгд".charCodeAt(0) == 224) alert("Yes!");
Но все равно ничего не работает. Я бьюсь над этим весь день и скоро выброшусь из окна. В чем проблема?!
 
Проблема в том, что в кодировке windows-1251 находится не документ, а файл на диске. А в браузере он уже в UTF. Все строки, которые были заданы в коде, перекодированы в UTF, все строки, которые вводятся в поля формы, кодируются в UTF. А в UTF код русского символа “а” отнюдь не такой.
 
Вопрос 2
 
У меня есть страница:
 
HTML
<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Тест</title>
    <meta http-equiv="content-type" content="text/html; charset=windows-1251" />
<script type="text/javascript">
 function f() {
     if (window.XMLHttpRequest) {
         var req = new XMLHttpRequest;
     } else {
         try {
             var req = new ActiveXObject("Msxml2.XMLHTTP");
         } catch(e) {}
     }
     if (!req)
         return false;
 
     req.onreadystatechange = (function() {
         if ((req.readyState != 4) || (req.status !=  200) || (!req.responseXML) || (!req.responseXML.documentElement))
             return false;
         var dE = req.responseXML.documentElement;
         document.getElementById("test").firstChild.nodeValue =
             dE.getElementsByTagName("word").item(0).firstChild.nodeValue + ", " +
             dE.getElementsByTagName("len").item(0).firstChild.nodeValue;
         return true;
     } );
 
     req.open("GET", "./test.php?var=" + encodeURIComponent(document.getElementById("inp").value));
     req.send("");
 
     return true;
 
 }
</script>
  </head>
  <body>
    <input type="text" id="inp" value="" />
    <input type="submit" value="Ok" onclick="f()" />
    <div id="test">Loading...</div>
  </body>
</html>
 

Я ввожу какой-то текст в поле, нажимаю кнопку, и значение поля отправляется в запросе в файл test.php:
 
PHP
<?php
 header('content-type: text/xml; charset=windows-1251');
 print '<?xml version="1.0" encoding="windows-1251"?>';
?>
<response>
  <word><?php print htmlSpecialChars($_GET['var']); ?></word>
  <len><?php print strLen($_GET['var']); ?></len>
</response>

Сценарий возвращает значение поля и его длину, все это выводится на странице в отведенном под это месте. Когда я пишу на английском - все нормально. А когда на русском, вместо слова выводится невесть что, а длина содержит вообще непонятное число. Почему?
 
Ответ – все потому же. Значение поля в запросе отправляется уже в UTF. Возвращайте в заголовках ответа кодировку xml – utf, а для вычисления длины используйте mbstring. А еще лучше используйте везде на страницах и в сценариях UTF. Геморроя будет куда меньше.
 
JS-код в атрибутах
 
Некоторые атрибуты элементов могут содержать в качестве значения js-код. Это, например, обработчики событий (onclick, onmouseover и др.).
 
Здесь не нужно забывать тот момент, что эти атрибуты в первую очередь именно html-атрибуты, а уже потом js-код. Т.е. они обрабатываются, как и все другие атрибуты и должны соответствовать нужному формату. Например, неоднозначные символы желательно заменять на их html-сущности. Можно и не заменять. Браузеры, конечно, нам все простят, но иногда могут быть проблемы. Кстати, здесь есть и положительные места. Например, кошмар всех php-шников:
 
HTML
<form method="post" onsubmit="return cofirm('вы уверены, что хотите отослать форму?')">

Данный тег нужно вывести через print. Для ограничения значения атрибута onsubmit используются двойные кавычки, а для строки внутри него – одинарные. Для вывода через print придется применять кавычки еще раз, а внутри устраивать частокол из слешев. А можно иначе:
 
PHP
print '<form method="post" onsubmit="return cofirm("вы уверены, что хотите отослать форуму")">';

И все работает. Более того, именно так в теории и правильно.
 
Кстати, по идее, содержимое элемента <SCRIPT> так же должно обрабатываться перед выполнением, либо во избежание этого заключаться в секцию <[CDATA[]]>. Однако, услужливые браузеры содержимое данного тега не обрабатывают, позволяя нам не париться. Так что alert( 5 > 4 ) пройдет в атрибуте, но не в элементе сценария.
 
Атрибуты типа href
 
Есть некоторые атрибуты, которые содержат URL. В частности, это href у элемента <A>. Спрашивается, что нужно применять к ним: html-кодирование или url-кодирование. Ответ – как любой URL, значение атрибута href должно быть представлено в заданном для url формате (со всякими %). Т.е., например в PHP, все значения параметров должны быть пропущены через rawUrlEncode(). А вот являясь html-атрибутом, в исходном тексте html оно должно соответствовать формату html (htmlSpecialChars()). Т.е. например ссылка с двумя параметрами:
http[nobbc]://[/nobbc]site.com/page.php?x=Вася&y=Петя по-правильном должна выглядеть, как
http[nobbc]://[/nobbc]site.com/page.php?x=%D0%92%D0%B0%D1%81%D1%8F&y=%D0%9F%D0%B5%D1%82%D1%8F
 
А html-ссылка так:
<a href=”http:[nobbc]//site.com/page.php[/nobbc]?x=%D0%92%D0%B0%D1%81%D1%8F&amp;y=%D0%9F%D0%B5%D1%82%D1”>
 
Ага, именно &amp; должен разделять параметры, а не &.
 

Загрузка документа
 
document.write()
 
Очень многие любят заявлять о том, что document.write() устарел и вместо его следует использовать DOM. На самом деле это совершенно различные вещи, хотя документрайтом злоупотреблять все равно не следует.
 
Рассмотрим этап загрузки страницы. Именно здесь происходит разбор исходного кода. Вернее разбор потока. Разборщик получает на вход поток html-кода, который постепенно разбирает и отрисовывает. Подвал страницы еще может формироваться на сервере, а пользователь уже наслаждается анимированными баннерами в шапке сайта.
 
Один из важных моментов здесь, это то, что, получив очередным элементом, элемент <SCRIPT>, браузер исполняет его. А одной из возможностей javascript на данном этапе является возможность писать в поток кода. Что и делает document.write().
 
HTML
<div style="border: 1px solid #000000">
One
<script type="text/javascript">
    if (confirm("Закрыть нам этот несчастный див прямо сейчас?")) {
        document.write("</div><div>");
    }
</script>
Two
</div>

Т.е. document.write() просто выводит код, который идет, как часть потока и обрабатывается разборщиком. Соответственно наступает момент, когда поток иссякает, конечное дерево сформировано окончательно и поток закрывается.
 
Далее вопрос. У меня при щелчке по кнопке вызывается document.write(), который выводит некоторый текст. При этом вся страница становится белой и на ней остается только этот текст. Что это?
 
Встречный вопрос. А куда ты вообще хочешь, чтобы document.write() чего-то выводил? Конечно, к моменту щелчка, разбор, скорее всего, закончен и поток закрыт. Все что остается document.write() это грохнуть текущий документ и открыть новый поток. Что он и делает.
 
defer
 
Как уже сказано, натыкаясь в процессе разбора на элемент <SCRIPT>, браузер выполняет его, после чего продолжает разбирать входной код дальше. <SCRIPT> же может не непосредственно содержать код, а ссылаться на js-файл. В этом случае браузер запрашивает файл, ждет загрузки, выполняет его, после чего продолжает отрисовывать документ. Обратите внимание, пока файл не будет загружен, ничего больше в браузер выводиться не будет. Если это файл с удаленного сервера (например, баннер), да еще тот сервер намертво лежит (что с ними часто бывает), то браузер может зависнуть очень надолго.
 
Для решения данной проблемы есть атрибут defer. Ставя его в элементе <SCRIPT> вы указываете, что в загружаемом сценарии нет работы с потоком кода, поэтому он может загружаться параллельно странице. К сожалению, баннеры пользуют обычно именно document.write().
 
На вопрос: “для ускорения загрузки я поставил у загружаемого сценария defer, но при этом перестал работать document.write()” предлагаю читателю ответить самостоятельно.
 
Доступ к документу до полной загрузки
 
Ну и напоследок детская, но очень распространенная ошибка:
 
HTML
<script type="text/javascript">
document.getElementById("divid").innerHTML = "Текст";
</script>
<div id="divid"></div>

Почему не получается записать в слой? Потому что, на тот момент, когда выполняется код, этого элемента еще нет. Потому что код с его разметкой не дошел до разборщика и не был преобразован в узел дерева документа.
 
Кстати, чисто теоретически, работа через DOM, даже с уже существующими элементами, до полной загрузки, может иметь проблемы, т.к. дерево документа еще не сформировано полностью. На практике такие проблемы, мягко скажем, редки.
« Последнее редактирование: 04 Октябрь, 2007, 09:02:31 от vasa_c » Записан

cage    ↓ 
02 Сентябрь, 2007, 09:13:50 , спустя 2 часа 54 минуты 40 секунд
Если статью написал сам,то респект
Записан
vasa_c    ↓ 
02 Сентябрь, 2007, 09:23:58 , спустя 10 минут 8 секунд
НЕ ХУЕТА! ХУЕТА!

Группа: в ухо

Карма: 81
Сообщений: 2459
Сила слова: 3.29

Давно уже. С пхпфорума перетащил.
Записан

cage    ↓ 
02 Сентябрь, 2007, 09:32:07 , спустя 8 минут 9 секунд
молодец,статья классная
Записан
aligator    ↓ 
03 Сентябрь, 2007, 06:17:14 , спустя 20 часов 45 минут 7 секунд
НЕ ХУЕТА! ХУЕТА!


Карма: 2
Сообщений: 52
Сила слова: 3.85

thanks
Записан

Страниц: [1]
Печать
 

Перейти в: