Кросдоменная передача данных между html-страницами

Автор: Вячеслав Гринин | веб-программирование, веб-разработка | 29 Апр 2010 3:39 пп

Итак, представим себе ситуацию, что на некотором сайте в некоторой форме есть поле, в которое нужно ввести логин пользователя, но не свой собственный, а чужой логин, предположим, пользователя, которого надо добавить в друзья или в черный список. Но посетитель может не помнить наизусть, как пишется этот логин, а потому мы сделаем так, чтобы он мог выбрать его из списка, причем список этот должен открыться в отдельном окне и там помимо списка логинов пользователей должны отображаться еще и их фотографии, ФИО, возраст и т.д. Предположим, что мы даже создали такую страницу со списком пользователей. Возникает вопрос – как передать из одного окна браузера в другое окно некоторые данные (в данном случае это – логин пользователя)?

Справочник по JavaScript и объектной документной модели DOM говорит нам, что для открытия нового окна нужно использовать метод window.open(), а для доступа из “дочернего” окна в “родительское” (то есть то, которое и породило новое окно) нужно использовать указатель opener. Рассмотрим этот факт на примере:

index.htm

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <title>Главное окно</title>
 <script>
 function openWindow() {
 window.open("http://easy4web.ru/samples/transfer/popup.htm",
    "contents", "toolbar=no", "status=no");
 }
 </script>
 </head>
 <body>
 <input type="button" onclick="openWindow()"
    value="Показать диалог"/><br />
 <input type="text" id="data" />
 </body>
</html>

popup.htm

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>Диалоговое</title>
 <script>
 function transferData() {
 opener.document.getElementById("data").value =
    document.getElementById("inp").value;
 window.close();
 }
 </script>
 </head>
 <body>
 <input type="text" id="inp" name="inp"/>
 <input type="button" value="OK" onclick="transferData()"/>
 </body>
</html>

В главной странице мы видим кнопку, по нажатии на которую открывается новое окно, и TEXTBOX с айдишником data, в него-то и будет попадать текст, введенный в диалоговом окне.

А вот и диалоговое окно, в нем мы видим TEXTBOX и кнопку по нажатию на которую текст введенный в текстовое поле присваивается текстовому полю data из главного окна. А доступ к нему мы и получаем при помощи указателя opener.

Вот здесь (Передача данных между окнами в рамках одного домена) вы можете протестировать работу этого алгоритма.

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

А хочу я теперь рассказать о том, как быть если окна эти расположены в разных доменах. Когда такое может произойти? Предположим, есть Ваш форум, а есть специализированный сервис для загрузки и хранения фотографий и вот теперь владелец форума хочет договориться с владельцем фото-сервиса, что на форму он разместит кнопку “Добавить изображение”, которая будет открывать окно созданное в рамках фото-сервиса, в этом окне пользователь загрузит фотографии, а затем по нажатию кнопки “ОК” ссылки на фотографии скопируются в окно редактирования сообщения на форуме. Как видим, здесь есть два окна в разных доменах и нам нужно передать текст из одного окна в другое.
“Что тут сложного?” – спросите вы. А давайте попробуем.

Невозможность передачи данных между окнами в разных доменах

При попытке передачи данных между окнами получим ошибку Error: Access is denied.. Происходит это потому что прежде чем получить доступ к любому методу или свойству объекта opener браузер сравнит домен, в котором существует этот объект и домен, из которого происходит вызов собственно метода или свойства объекта opener. И если доменные имена не совпадают, будет возбуждено исключение “Доступ запрещен”.

Решить эту проблему можно. И мы решим ее без использования каких-либо серверных технологий, только силами JavaScript. Это становится возможным, если мы узнаем еще вот какую тонкость. При изменении свойства location.href в порожденном окне в него загрузится страница, заданная ссылкой, но значение указателя opener не изменится, он так и будет продолжать указывать на породившее его главное окно. А теперь представим себе, что мы в диалоговое окно загрузили все ту же главную страницу, или любую другую но с того же домена, где лежит главная страница, а после этого обратились к объекту opener, теперь домены диалогового окна и объекта opener снова совпадают, а значит исключение возбуждено не будет.

А теперь – время для исходников, иллюстрирующих пример:

index.htm

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>Главное окно</title>
 <script>
 function openWindow() {
 var par = "?loc=" + location.href + "&id=data";
 window.open("http://easyapi.ru/easy4web/transfer/popup2.htm" + par,
    "contents", "toolbar=no", "status=no");
 }
 function getDataFromUrl() {
 var txt_id, data;
 var url = location.href;
 var query = url.split("?")[1];
 if(query) {
 var params = query.split("&");
 for(var i = 0; i < params.length; i++) {
 var keyval = params[i].split("=");
 if(keyval[0] == "data") {
 data = keyval[1];
 }
 if(keyval[0] == "id") {
 txt_id = keyval[1];
 }
 }
 }
 if(txt_id && data) {
 opener.document.getElementById(txt_id).value = data;
 close();
 }
 }
 getDataFromUrl();
 </script>
 </head>
 <body>
 <input type="button" onclick="openWindow()"
    value="Показать диалог"/><br/>
 <input type="text" id="data" />
 <script>
 getDataFromUrl();
 </script>
 </body>
</html>

Итак, здесь мы видим, что процедура openWindow() все также открывает диалоговое окно, однако теперь она передает ему параметры: loc – указывающий на URL самой порождающей страницы; id – айдишник текстбокса, в который будет вставлен текст из дочернего окна.
А еще мы видим, что после загрузки контента главной страницы вызывается функция getDataFromUrl(), которая проверяет, есть ли GET-параметры id и data (айдишник текстбокса и текст, который мы в него будем вставлять). Параметры эти при изначальной загрузке страницы не заданы, а сначит при первой загрузке страницы код обновляющий содержимое текстбокса не выполнится. А когда он выполнится мы узнаем после того, как рассмотрим исходники диалогового окна.

popup.htm

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 <title>Диалог</title>
 <script>
 function transferData() {
 var url = location.href;
 var query = url.split("?")[1];
 if(query) {
 var params = query.split("&");
 for(var i = 0; i < params.length; i++) {
 var keyval = params[i].split("=");
 if(keyval[0] == "loc") {
 var loc = keyval[1];
 }
 if(keyval[0] == "id") {
 var txt_id = keyval[1];
 }
 }
 }
 if(loc && txt_id) {
 location.href = loc + "?id=" + txt_id +
    "&data=" + document.getElementById("inp").value;
 }
 }
 </script>
 </head>
 <body>
 <input type="text" id="inp" name="inp"/>
 <input type="button" value="OK" onclick="transferData()"/>
 </body>
</html>

Здесь при клике по кнопке выполнится функция transferData(), которая сначала извлечет из адресной строки параметры loc и id, те самые, которые мы передали окну при его порождении. И если эти параметры есть – присвоим location.href адрес страницы loc(главной страницы), а в GET-параметры ей передадим id и data(айдишник текстбокса и данные, которые мы в него запишем). При этом в текущее окно (диалоговое) загрузится содержимое главнйо страницы и выполнится та самая функция getDataFromUrl() которая на этот раз извлечет все необходимые параметры из GET-строки и выполнит метод getElementById() объекта opener.document.

Вот и весь секрет кросдоменной передачи данных между окнами.

Вот здесь (Передача данных между окнами в разных доменах) можно протестировать работу вышеприведенного алгоритма.

Замечу, что у всех приведенных здесь решений есть существенный недостаток – корректно передаются только символы английского алфавита, цифры и знаки препинания. Решение этой проблемы оставлю на одну из следующих статей.

Прочтите еще:

Плагин, отображающий руны викингов

Автор: Вячеслав Гринин | веб-разработка | 16 Сен 2009 2:23 пп

Зацените плагин. Он преобразует входную строку на латинице в последовательность псевдо-рунов в виде картинок.


Прочтите еще:

    None Found

XML+XSLT на примере XML-погоды от gismeteo.ru

Автор: Вячеслав Гринин | веб-программирование, веб-разработка | 06 Авг 2009 5:14 пп

Итак, выполняя свое обещание, публикую статью, посвященную отображению информера погоды gismeteo.ru.

Как вы помните, в прошлый раз в статье Парсинг XML в JavaScript на примере XML-погоды от gismeteo.ru мы написали длинный (в нескольких местах запутанный и непонятный) скрипт на JavaScript, который радостно преобразовывал XML-Данные от ГИС-метео в HTML-формат, который мы и отображали в нашем браузере.

Этот вариант имел ряд недостатков:
1) При отключенном JavaScript в браузере пользователя мы вообще ничего не увидим.
2) Шаблоны, используемые в этом скрипте были настолько запутанные, что иногда и я сам терялся в том, что значит каждый из тегов этого шаблона.
3) И вообще это не модно и несовременно!

Ведь есть технология лучше: XML+XSLT !

Вот выдержка из wikipedia:

XSLT (Extensible Stylesheet Language Transformations) — часть спецификации XSL, задающая язык преобразований XML-документов. Спецификация XSLT является рекомендацией W3C.

При применении таблицы стилей XSLT, состоящей из набора шаблонов, к XML-документу (исходное дерево) образуется конечное дерево, которое может быть другой XML-структурой, HTML-документом или обычным текстом. Правила выбора (и, отчасти, преобразования) данных из исходного дерева пишутся на языке запросов XPath.

XSLT имеет множество различных применений, в основном в области web-программирования и генерации отчётов. Одной из задач, решаемых языком XSLT, является отделение данных от их представления, как часть общей парадигмы MVC (англ. Model-view-controller). Другой стандартной задачей является преобразование XML-документов из одной XML-схемы в другую.
В общем, XSLT – это тоже XML особого вида, главной целью которого является преобразование XML-данных из одного вида в другой.

Собственно, эта статья подразумевается именно как практическое введение в XSLT, а поэтому я сразу приведу результирующий XSLT-шаблон, а затем буду его построчно объяснять. Вот он.

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>

 <!--Шаблон, преобразующий номер дня недели в его текстовое
представление-->
 <xsl:template name="get-day-of-the-week-abbreviation">
 <xsl:param name="day-of-the-week"/>
 <xsl:choose>
 <xsl:when test="$day-of-the-week = 1">Вс</xsl:when>
 <xsl:when test="$day-of-the-week = 2">Пн</xsl:when>
 <xsl:when test="$day-of-the-week = 3">Вт</xsl:when>
 <xsl:when test="$day-of-the-week = 4">Ср</xsl:when>
 <xsl:when test="$day-of-the-week = 5">Чт</xsl:when>
 <xsl:when test="$day-of-the-week = 6">Пт</xsl:when>
 <xsl:when test="$day-of-the-week = 7">Сб</xsl:when>
 <xsl:otherwise>?</xsl:otherwise>
 </xsl:choose>
 </xsl:template>

 <!--Шаблон, преобразующий порядковый номер месяца в его
текстовое представление-->
 <xsl:template name="get-month-name">
 <xsl:param name="month"/>
 <xsl:choose>
 <xsl:when test="$month = 1">янв</xsl:when>
 <xsl:when test="$month = 2">фев</xsl:when>
 <xsl:when test="$month = 3">мар</xsl:when>
 <xsl:when test="$month = 4">апр</xsl:when>
 <xsl:when test="$month = 5">май</xsl:when>
 <xsl:when test="$month = 6">июн</xsl:when>
 <xsl:when test="$month = 7">июл</xsl:when>
 <xsl:when test="$month = 8">авг</xsl:when>
 <xsl:when test="$month = 9">сен</xsl:when>
 <xsl:when test="$month = 10">окт</xsl:when>
 <xsl:when test="$month = 11">ноя</xsl:when>
 <xsl:when test="$month = 12">дек</xsl:when>
 <xsl:otherwise>?</xsl:otherwise>
 </xsl:choose>
 </xsl:template>

 <!--Шаблон, показывающий текстовое представление облачности-->
 <xsl:template name="get-cloudiness">
 <xsl:param name="cloudiness"/>
 <xsl:choose>
 <xsl:when test="$cloudiness = 0">ясно</xsl:when>
 <xsl:when test="$cloudiness = 1">малооблачно</xsl:when>
 <xsl:when test="$cloudiness = 2">облачно</xsl:when>
 <xsl:when test="$cloudiness = 3">пасмурно</xsl:when>
 <xsl:otherwise>?</xsl:otherwise>
 </xsl:choose>
 </xsl:template>

 <!--Шаблон, показывающий текстовое представление атмосферных явлений-->
 <xsl:template name="get-precipitation">
 <xsl:param name="precipitation"/>
 <xsl:choose>
 <xsl:when test="$precipitation = 4">дождь</xsl:when>
 <xsl:when test="$precipitation = 5">ливень</xsl:when>
 <xsl:when test="$precipitation = 6">снег</xsl:when>
 <xsl:when test="$precipitation = 7">снег</xsl:when>
 <xsl:when test="$precipitation = 8">гроза</xsl:when>
 <xsl:when test="$precipitation = 10">без осадков</xsl:when>
 <xsl:otherwise>?</xsl:otherwise>
 </xsl:choose>
 </xsl:template>

 <!--Корневой шаблн результирующей страницы-->
 <xsl:template match="/">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <title>gis meteo informer</title>
 <style type="text/css"></style>
 </head>
 <body>
 <!--Здесь мы выбираем только самый первый(ближайший) прогноз
под номером 1 -->
 <xsl:apply-templates select="MMWEATHER/*/TOWN/FORECAST[1]"/>
 </body>
 </html>
 </xsl:template>

 <!--Базовый шаблон, отображающий информер-->
 <xsl:template match="MMWEATHER/*/TOWN/FORECAST">
 <div id="informer0">
 <table cellspacing="0" cellpadding="1" width="100%"
 border="0" id="gmtbl5">
 <tr>
 <td width="100%" colspan="3">
 <table cellspacing="0" cellpadding="0" width="100%">
 <tr>
 <td height="20" width="100%" id="gmtdttl5" colspan="3">
 <b><a target="_blank" id="tgmtdttl5" href="http://www.gismeteo.RU/city/daily/4368">Москва</a></b>
 </td>
 </tr>
 </table>
 </td>
 </tr>
 <tr>
 <td width="15%">
 <!--Здесь вставляем шаблон, отображающий атмосферные явления-->
 <xsl:apply-templates select="PHENOMENA"/>
 </td>
 <td width="85%" id="tgmtdtext50">
 <xsl:value-of select="@day"/>
 <!--Далее идет вызов шаблонов, отображающих дату в удобном формате-->
 <xsl:call-template name="get-month-name">
 <xsl:with-param name="month" select="@month" />
 </xsl:call-template> - <xsl:call-template name="get-day-of-the-week-abbreviation">
 <xsl:with-param name="day-of-the-week" select="@weekday" />
 </xsl:call-template><br/>
 <!--Здесь вставляем шаблон, отображающий температуру-->
 <b><xsl:apply-templates select="TEMPERATURE"/></b><br/>
 <a target="_blank" id="lgmtdtext50" href="http://www.gismeteo.RU">
GISMETEO.RU</a>
 </td>
 </tr>
 <tr>
 <td colspan="3" heigth="5"/>
 </tr>
 </table>
 </div>
 </xsl:template>

 <!--Шаблон, отображающий температуру-->
 <xsl:template match="TEMPERATURE">
 <xsl:value-of select="@min"/>..<xsl:value-of select="@max"/> °C
 </xsl:template>

 <!--Шаблон, отображающий атмосферные явления-->
 <xsl:template match="PHENOMENA">
 <xsl:element name="img">
 <xsl:attribute name="height">60</xsl:attribute>
 <xsl:attribute name="width">59</xsl:attribute>
 <xsl:attribute name="alt">
 <xsl:call-template name="get-cloudiness">
 <xsl:with-param name="cloudiness" select="@cloudiness" />
 </xsl:call-template>,
 <xsl:call-template name="get-precipitation">
 <xsl:with-param name="precipitation" select="@precipitation" />
 </xsl:call-template>
 </xsl:attribute>
 <xsl:attribute name="src">http://informer.gismeteo.ru/getcode/html/images/animbg/<xsl:value-of select="@cloudiness"/>.gif</xsl:attribute>
 </xsl:element>
 </xsl:template>

</xsl:stylesheet>

Собственно, строки 1 и 3 – стандартные заголовки для XML-файла. Замечу лишь, что мы используем версию XSLT 1.0. Есть еще и 2.0 версия, но о ней мы поговорим в следующих статьях.

Строка 4 говорит, что результирующий вывод будет HTML.

Строки 7 и 19 задают именованный шаблон get-day-of-the-week-abbreviation, который в будущем мы будем использовать как функцию преобразования номера дня недели в его строковое представление.  В строке 8 задается входно параметр day-of-the-week этого шаблона, нечто вродеаргумента функции. Это собственно сам номер дня недели.

В строках 9 и 18 объявляется секция <xsl:choose>, которая, тестируя некторое условие, вставляет в зависимости от результатов тестирования булева выражения соответствующую строку. Сами проверки заключены в теги <xsl:when></xsl:when> где само тестируемое условие задано аттрибутом test

Собственно, все вплоть до строки 65 – это вспомогательные шаблоны преобразований кодов в их текстовое представление, думаю, что в особых комментариях эти шаблоны не нуждаются.

Строки 68-80, это корневой шаблон, то есть тот шаблон, который будет вызван один раз для корневого элемента исходного XML-файла. Как мы видим, это просто вполне стандартный заголовок HTML-файла. Элемент(ы), для которого(которых) шаблон сработает, задается в атрибуте match шаблона. В нашем случае это “/”. Вообще селекторы (то есть то, что расположено в атрибуте match) пишутся на языке XPath, прочитать про основы которого можно в Википедии, вот здесь. Сам контент результирующей страницы вставляется между тегами <body></body> при помощи конструкции apply-templates.

В строках 83-120 задается базовый шаблон, в котором, собственно можно видеть всю разметку информера. Замечу только, что атрибут atname текущего элемента вставляется в результирующий документ с помощью конструкции <xsl:value-of select=“@atname”/>, как в строке 103, например.

Вызов именованного шаблона осуществляется при помощи такой разметки:

<xsl:call-template name="get-month-name">
<xsl:with-param name="month" select="@month" />
</xsl:call-template>

Как видно, атрибутом name тега xsl:call-template задается имя вызываемого шаблона (помните, мы объявляли их в начале файла?), а внутри секции call-template при помощи тегов xsl:with-param задаются сами подаваемые в шаблон параметры.

А вот еще одна маленькая хитрость. Конструкция:

<xsl:element name="img">
<xsl:attribute name="height">60</xsl:attribute>
<xsl:attribute name="width">59</xsl:attribute>
</xsl:element>

Она позволяет вставить в результирующий документ элемент IMG, с атрибутами width и height, да и в принципе, любой другой элемент, с любыми другими атрибутами. В данном примере будет вставлен элемент <img width=”59″ height=”60″/>

И чуть не забыл! В самом XML-файле необходимо указать шаблон, посредством которого он будет обрабатываться. Это нужно только если вы собираетесь доверить XSLT-парсинг самому браузеру, в котором вы откроете XML-файл. О других методах парсинга расскажу в следующих статьях.

Вот, собственно и все, что я хотел рассказать.
Ссылка на результат работы здесь.
Ссылка на архив с работающим примером – здесь.

Прочтите еще:

Хаки для браузеров

Автор: веб-дизайнер | веб-дизайн, веб-разработка | 15 Апр 2009 9:58 дп

Сегодня мне хотелось бы поговорить о так называемых «css-хаках». CSS-хаки используют тогда, когда один конкретный браузер надо заставить отображать некий блок разметки не так, как хотелось бы самому браузеру. Сразу оговорюсь, что использовать хаки нужно только тогда, когда это действительно необходимо. Скажем, всеми нами горячо любимый Internet Explorer (седьмой версии) имеет свои, сугубо специфичные представления о работе со списками. И иногда надо только ему объяснить, что он неправ:-)
Вообще задача раздельных CSS для разных браузеров имеет несколько путей решения. Один из них — встроить в <head></head> php-, или java-скрипт, определяющий тип браузера, и отсылающий конкретный браузер к предназначенной для него стилевой таблице. Второй — если проблемы возникают только с IE — так называемые «условные комментарии». В <head></head> задается конструкция вида:

<!–[if IE]><link rel=”stylesheet” href=”css/ie.css” type=”text/css” media=”screen” /><![endif]–>

или для конкретных версий Explorer’а:
<!–[if IE7]><link rel=”stylesheet” href=”css/ie7.css” type=”text/css” media=”screen” /><![endif]–>
<!–[if IE6]><link rel=”stylesheet” href=”css/ie6.css” type=”text/css” media=”screen” /><![endif]–>

И, наконец, третий путь — CSS-хаки.

Самый удобный в плане хаков браузер – Firefox (и относящиеся к его семейству), так как имеет свою группу css-классов, начинающуюся с перфикса @-moz-
Например:

@-moz-document url-prefix() {
.style {background: #F00;}
}

css-rule, x:-moz-any-link{

}

Для Opera:

@media all and (-webkit-min-device-pixel-ratio:10000),
not all and (-webkit-min-device-pixel-ratio:0) {
.style {background: #F00;} /* только для Opera */
}

html:first-child {} /* только для Opera */
html:first-child .style {
background: #F00; /* только для Opera < 9.5 */
}

Также, можно создать специальный файл стилей для Opera и подключить его следующим образом:
<head>
<link rel=”stylesheet” href=”css/opera.css” type=”opera/css” media=”screen” />
&lt/head>

Google Chrome и Safari по способу работы с блочной моделью напоминают Opera и с высокой долей вероятности можно предполагать, что если ваша верстка нормально выглядит в Опере, то и в Хроме с Сафари с нею тоже все будет нормально. Однако, на всякий случай, хаки:

body:last-child:not(:root:root) .style {
background: #F00;
}

Ну, и наконец, хак для IE. Почему только один? По опыту решения конкретных задач могу сказать, что все проблемы верстки, с которыми мне приходилось сталкиваться, были одинаковыми у наиболее распространенных версий IE (6,7). Поэтому я пользовался (если пользовался) универсальным хаком, так называемым «holy hack»:

html {
background: #f00; (это понимают все браузеры)
* background: #0f0; (а это только ослики)
}

Напоследок напомню, что практически все хаки влияют на валидность полученного кода, поэтому, если есть возможность – надо обходиться без них. А такая возможность есть почти всегда, и исключение составляет только горячо любимый всеми нами Internet Explorer:-)

Прочтите еще:

Облегчая себе верстку.

Автор: веб-дизайнер | веб-разработка | 17 Мар 2009 8:47 дп

Всем нам давно известно, что каждый браузер имеет свои дефолтные значения CSS для многих объектов, таких как, скажем, список, параграф, границы и тому подобное (подробнее, например, тут). Для того, чтобы верстать действительно кроссбраузерно нам необходимо сбросить стандартные значения на некий общий знаменатель, а потом прописать все заново.

Некий сознательный гражданин Эрик Майер предложил следующую версию стилей для обнуления всех значений:

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
background: transparent;
}
/* Не забываем описать стили для :focus! */
:focus {
outline: 0;
}
body {
line-height: 1;
color: black;
background: white;
}
ol, ul {
list-style: none;
}
/* Для таблиц все еще надо прописывать ‘cellspacing=”0″‘ в коде */
table {
border-collapse: collapse;
border-spacing: 0;
}
caption, th, td {
text-align: left;
font-weight: normal;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: “”;
}
blockquote, q {
quotes: “” “”;
}

В принципе, само по себе это правило достаточно хорошо за парой исключений. А именно – спорным является убирание рамки для псевдокласса :focus (это который рисует рамку вокруг нажатых ссылок), так как такими ссылками нельзя будет управлять с клавиатуры, нельзя будет понять, что они в данный момент в фокусе. Также спорным выглядит использование парных кавычек “”, так как в русской типографике используются обычно кавычки елочки «», вызываемые кодами &#171; и &#187;

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

Прочтите еще:

Позже »