Статьи

Скругленные углы с CSS и JavaScript

Закругленные углы являются одним из наиболее часто используемых методов CSS. Как и во многих вещах в CSS, существуют различные способы решения этой проблемы. В этой статье я расскажу о плюсах и минусах некоторых распространенных методик и представлю новую методику, использующую как CSS, так и JavaScript.

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

<table width="200" cellpadding="0" cellspacing="0"> <tr>  <td width="15"><img src="tl.gif" alt="" /></td>  <td bgcolor="#1b5151"></td>  <td width="15"><img src="tr.gif" alt="" /></td> </tr> <tr bgcolor="#1b5151">  <td>&nbsp;</td>  <td>Content goes here</td>  <td>&nbsp;</td> </tr> <tr bgcolor="#1b5151">  <td><img src="bl.gif" alt="" /></td>  <td></td>  <td><img src="br.gif" alt="" /></td> </tr> </table> 

Несколько лет назад это было бы приемлемым решением. Сегодня это отвратительный взлом: это очень много избыточной разметки для относительно неважного визуального оформления. Фактически, приведенный выше код даже не будет работать должным образом в документах, обслуживаемых с использованием строгого типа документа: под угловыми изображениями будут появляться небольшие пробелы, вызванные тем, что изображения являются встроенными элементами и, следовательно, оставляют пространство под изображением «хвосты» на буквы, такие как «у» и «J». Решение, как объяснил Эрик Мейер в книге «Изображения, таблицы и загадочные пробелы» , заключается в добавлении следующего правила в таблицу стилей:

 td img { display: block; } 

Это дает желаемый результат, как показано здесь .

Но теперь мы используем CSS-хаки, чтобы исправить некрасивые хаки таблиц! Давайте посмотрим, как реализовать тот же эффект, используя только CSS.

Как правило, любое декоративное изображение должно быть реализовано как фоновое изображение CSS на существующем элементе страницы, а не вставляться в саму страницу с помощью <img> . Легко определить, является ли изображение декоративным или содержит фактический контент: спросите себя, не повлияет ли отсутствие изображения на общее содержание страницы. В случае закругленных углов ответ, очевидно, нет.

Фоновые изображения CSS — удивительно мощные вещи. Вам нужно только взглянуть на множество замечательных дизайнов, представленных в CSS Zen Garden, чтобы убедиться в этом. Используя CSS, фоновое изображение может быть применено к любому элементу на странице. Кроме того, это может повторяться горизонтально, вертикально или вообще не повторяться; он может быть расположен в пределах фоновой области изображения с использованием абсолютных измерений или относительно одного из четырех углов; это может быть даже сделано, чтобы оставаться фиксированным на месте, когда содержимое элемента прокручивается. К сожалению, CSS 2 накладывает одно небольшое, но существенное ограничение: вы можете применять только одно фоновое изображение к каждому элементу на странице. Чтобы правильно отобразить закругленные углы на <div> нам нужно применить четыре фоновых изображения, по одному в каждом углу.

Фиксированные коробки ширины

Если ширина ящика, к которому мы применяем декоративные углы, является фиксированной, половина проблемы уже решена. Если мы знаем, что поле всегда будет шириной 200 пикселей, вместо создания четырех фоновых изображений (по одному для каждого угла), мы можем создать два: одно для верхней части окна и одно для нижней части. Теперь задача сводится к применению двух фоновых изображений для нашего <div> . Пришло время воспользоваться нашей разметкой.

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

 <div class="rounded"> <h3>Exciting features!</h3> <p>Your new Widget2000 will...</p> <ul> <li>... clean your shoes</li> <li>... walk your dog</li> <li>... and balance your cheque book!</li> </ul> </div> 

Довольно просто, а? Заголовок блока находится в <h3> (я предполагаю, что <h1> и <h2> уже использовались в дальнейшем в иерархии страницы), а последующий контент — это абзац и неупорядоченный список. Ключ к решению нашей двух основных проблем лежит в <h3> , который находится прямо вверху окна. Все, что нам нужно сделать, это применить фоновое изображение к верхней части <h3> , а другое к нижней части содержащего <div> , и эффект будет завершен:

 div.rounded { width: 200px; background: #1b5151 url(200pxbottom.gif) no-repeat bottom center; padding-bottom: 15px; } div.rounded h3 { padding-top: 15px; background: transparent url(200pxtop.gif) no-repeat top center; } 

Нажмите здесь, чтобы увидеть результаты.

Хорошо структурированные документы, как правило, полны таких хуков, которые можно осторожно использовать для применения нескольких фонов и достижения определенных визуальных эффектов. Обучение их идентификации является важной частью работы с CSS.

Вложенные элементы

Применение четырех фонов к одному элементу div все еще недоступно. Но что, если мы вложили четыре div, по одному для каждого фона? Это решает нашу проблему, но происходит за счет дополнительной разметки без конструктивной ценности:

 <div class="rounded"><div><div><div> Content goes here </div></div></div></div> 

И в CSS:

 div.rounded { width: 200px; background: #1b5151 url(tr.gif) no-repeat top right; } div.rounded div { background: transparent url(tl.gif) no-repeat top left; } div.rounded div div { background: transparent url(br.gif) no-repeat bottom right; } div.rounded div div div { background: transparent url(bl.gif) no-repeat bottom left; padding: 15px; } 

Код отображается как показано здесь .

Должно быть понятно, что здесь происходит. Каждому из четырех делителей назначается фоновое изображение с закругленными углами, расположенное в верхнем правом, верхнем левом, нижнем правом и нижнем левом углу соответственно. Несмотря на то, что ширина содержащего элемента div равна 200px, его можно также легко установить на что-то более гибкое для использования с жидкими конструкциями — углы все равно будут работать, независимо от того, насколько большим или маленьким был элемент div.

Теперь у нас есть решение проблемы, которое использует гораздо меньше разметки, чем в исходном примере таблиц. Но он все еще не идеален: он использует три дополнительных элемента div, которые ничего не добавляют к общей структуре документа. Можем ли мы сделать лучше? Пришло время взглянуть на JavaScript.

Использование DOM

Используя JavaScript и DOM, можно манипулировать структурой документа после его загрузки браузером. Закругленные углы — это эффект представления, который можно скрыть от пользовательских агентов, не являющихся JavaScript, без какого-либо существенного сокращения их общего восприятия сайта, поэтому нет никаких этических проблем с использованием JavaScript для такого рода преобразований. Наше окончательное решение потребует только одного <div> в исходном документе. Мы будем использовать JavaScript для динамического добавления трех посторонних элементов, необходимых для эффекта закругленного угла.

Вот разметка:

 <div class="rounded"> Content goes here. </div> 

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

Теперь вот JavaScript:

 function roundedCorners() { var divs = document.getElementsByTagName('div'); var rounded_divs = []; /* First locate all divs with 'rounded' in their class attribute */ for (var i = 0; i < divs.length; i++) {   if (/broundedb/.exec(divs[i].className)) {     rounded_divs[rounded_divs.length] = divs[i];   } } /* Now add additional divs to each of the divs we have found */ for (var i = 0; i < rounded_divs.length; i++) {   var original = rounded_divs[i];   /* Make it the inner div of the four */   original.className = original.className.replace('rounded', '');   /* Now create the outer-most div */   var tr = document.createElement('div');   tr.className = 'rounded2';   /* Swap out the original (we'll put it back later) */   original.parentNode.replaceChild(tr, original);   /* Create the two other inner nodes */   var tl = document.createElement('div');   var br = document.createElement('div');   /* Now glue the nodes back in to the document */   tr.appendChild(tl);   tl.appendChild(br);   br.appendChild(original); } } /* Run the function once the page has loaded: */ window.onload = roundedCorners; 

Сценарий состоит из двух логических разделов. Первый раздел перебирает все элементы <div> в документе, создавая массив тех элементов, которые содержат 'rounded' в своем атрибуте class (помните, что элементы могут иметь несколько классов, разделенных пробелами). Вторая часть сценария проходит по очереди каждый из этих элементов, создавая три дополнительных элемента div и оборачивая их вокруг оригинала. Давайте посмотрим на код для этого более подробно:

 original.className = original.className.replace('rounded', ''); 

Здесь мы полностью удаляем класс "rounded" из нашего исходного <div> . Причина этого станет ясна в CSS; по сути, мы не хотим, чтобы применяемые оригинальные стили больше влияли на этот элемент.

 var tr = document.createElement('div'); tr.className = 'rounded2'; 

Мы создали самый внешний <div> , который будет использоваться для применения фонового изображения в верхнем правом углу, а также общей ширины рамки. Обратите внимание, что мы установили класс в 'rounded2'; это будет определено в нашем CSS, с небольшими отличиями от 'rounded' класса, предоставляемого клиентам без поддержки JavaScript.

 /* Swap out the original (we'll put it back later) */ original.parentNode.replaceChild(tr, original); 

W3C DOM не предоставляет прямой метод замены узла в документе другим узлом. Вместо этого вы должны использовать метод replaceChild() узла, чтобы заменить одного из его дочерних узлов другим узлом. Полезный прием для замены рассматриваемого узла — получить доступ к своему собственному родителю с parentNode свойства parentNode , а затем использовать /#c#.replaceChild, чтобы заменить его на что-то другое. Если это не имеет смысла для вас, не беспокойтесь — просто представьте, что приведенная выше строка заменяет наш оригинальный узел новым узлом tr мы только что создали.

 /* Create the two other inner nodes */ var tl = document.createElement('div'); var br = document.createElement('div'); /* Now glue the nodes back in to the document */ tr.appendChild(tl); tl.appendChild(br); 

Теперь мы создали три новых элемента <div> и вставили их в документ. Все, что осталось, — это заново вставить наш оригинальный узел с его содержимым:

 br.appendChild(original); 

На этом этапе наше фактическое дерево документов почти идентично дереву в примере с четырьмя вложенными тегами <div> , с той лишь разницей, что внешний элемент имеет класс 'rounded2' вместо 'rounded' . Вот CSS:

 div.rounded { width: 170px; padding: 15px; background: #1b5151; } div.rounded2 { width: 200px; background: #1b5151 url(tr.gif) no-repeat top right; } div.rounded2 div { background: transparent url(tl.gif) no-repeat top left; } div.rounded2 div div { background: transparent url(br.gif) no-repeat bottom right; } div.rounded2 div div div { background: transparent url(bl.gif) no-repeat bottom left; padding: 15px; } 

Вот результат.

Первый набор правил для div.rounded используется только в браузерах, которые не выполняют JavaScript. Обратите внимание, что ширина составляет 170px, а отступ — 15px, что в сумме составляет 200px (ширина плюс отступы слева и справа). Если вам это нужно для работы в IE 5 / Windows, которая по-разному интерпретирует значения отступов, вам нужно будет применить позорную модель блочной модели . Вы уже видели второй набор правил в предыдущем примере.

Заглядывая вперед

Вышеупомянутая методика будет работать во всех современных браузерах и во всех будущих браузерах, которые поддерживают стандарты CSS2 и DOM 2. CSS 3 вводит ряд новых способов достижения этого эффекта, что сделает вышеуказанные методы устаревшими. Помимо встроенной поддержки закругленных углов (которая уже доступна в браузерах семейства Mozilla), в CSS имеется мощный псевдоэлемент :: outside , который позволяет вставлять дополнительные стилизуемые элементы аналогично примеру JavaScript, показанному в этой статье. Если этого недостаточно, изображения границ позволят практически любое украшение границ, о котором вы могли бы подумать.

К сожалению, пройдет много лет, прежде чем поддержка CSS 3 станет широко доступной. До тех пор JavaScript более чем способен справиться со слабостью.