Верстаем HTML/CSS календарь

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

Готовых скриптов в Сети много, разной степени навороченности и крутизны, но мне нужно было что-то простое и я не хотел использовать javascript, поэтому решил сделать сам. И вот что у меня получилось:

HTML/CSS календарь

Давайте посмотрим как делается симпатичный календарь с помощью HTML/CSS.
Как видно из скриншота, было довольно много ограничений, в основном по размерам. Фактически пришлось втиснуть весь месяц в кубик размерами 200х150 пикселей. Так как календарь верстался для уже готового сайта, то простора для творчества было совсем мало.

Начнем с кода. Мне показалось логичным сделать календарную структуру с помощью definition list.

Definition term я оставил пустым, так как заголовок был задан в бэкграунде. Месяц, названия дней недели и числа я описал списками, вложеными в definition description.

Собственно весь HTML:

<dl>
<dt> </dt>
<dd>
<p class="month">May 07</p>
</dd>
<dd>
<ul class="week">
<li>S</li>
<li>M</li>
<li>T</li>
<li>W</li>
<li>T</li>
<li>F</li>
<li>S</li>
</ul>
</dd>
<dd>
<ul class="days">
<li><а hrеf="#"> </a></li>
<li><а hrеf="#"> </a></li>
<li><а hrеf="#">1<span>Content 1</span></a></li>
<li><а hrеf="#">2<span>Content 2</span></a></li>
<li><а hrеf="#">3<span>Content 3</span></a></li>
<li><а hrеf="#">4<span>Content 4</span></a></li>
<li><а hrеf="#">5<span>Content 5</span></a></li>
<li><а hrеf="#">6<span>Content 6</span></a></li>
<li><а hrеf="#">7<span>Content 7</span></a></li>
<li><а hrеf="#">8<span>Content 8</span></a></li>
<li><а hrеf="#">9<span>Content 9</span></a></li>
<li><а hrеf="#">10<span>Content 10</span></a></li>
<li><а hrеf="#">11<span>Content 11</span></a></li>
<li><а hrеf="#">12<span>Content 12</span></a></li>
<li><а hrеf="#">13<span>Content 13</span></a></li>
<li><а hrеf="#">14<span>Content 14</span></a></li>
<li><а hrеf="#">15<span>Content 15</span></a></li>
<li><а hrеf="#">16<span>Content 16</span></a></li>
<li><а hrеf="#">17<span>Content 17</span></a></li>
<li><а hrеf="#">18<span>Content 18</span></a></li>
<li><а hrеf="#">19<span>Content 19</span></a></li>
<li><а hrеf="#">20<span>Content 20</span></a></li>
<li><а hrеf="#">21<span>Content 21</span></a></li>
<li><а hrеf="#">22<span>Content 22 Blah blah many content much more content</span></a></li>
<li>23</li>
<li>24</li>
<li>25</li>
<li>26</li>
<li>27</li>
<li>28</li>
<li>29</li>
<li>30</li>
<li>31</li>
</ul>
</dd>
<dd id="descr">Point cursor to some date</dd>
</dl>

Здесь я думаю стоти прояснить один момент. Внутри каждого элемента списка есть ссылка, текстом которой является само число и там же находится span, внутри которого находится текст подсказки, которая нужна нам при наведении мышки. У span‘а изначально прописано свойство display:none, чтобы его не было видно.

Так как span находится в ссылке (как Ленин в свое время :) ), то мы можем поменять ему свойства на событии hover. Все что нам нужно это поменять свойство display на block. Ну почти все. Этот способ называется Pure CSS Popups, который описал небезызвестный Eric Meyer.

Но рыжий Эрик схитрил(?) и не описал у себя баг, из-за которого способ не работает в IE6… Но Гугль как всегда рулит и я нашел решение этой проблемы. Суть его сводится к тому, что в a:hover ОБЯЗАТЕЛЬНО нужно указать какое-либо свойство, которое не присутствует в a:link, a:visited и a:active. Любое кроме color. Я выбрал text-indent: 0. Ура, заработало!
Есть еще один момент. Так как мне нужно было абсолютно позиционировать текст подсказки внутри блока, то для definition list я задал position:relative, не указывая явно top и left, а для span с подсказкой position:absolute и координаты.

Всем элементам списка я задал фиксированую ширину и высоту и float:left. С размерами подогнал так, чтобы в одну строку влезали только 7 чисел.

Смотрим на стили:

dl{
width: 214px;
height: 182px;
background: url("schedule-bg.gif") left top no-repeat;
margin: 0 auto;
position:relative;
}
dt{
height: 34px;
}

dd ul{
margin: 0;
}

dd ul li{
list-style: none;
margin: 3px 1px 1px 1px;
width: 26px;
height: 12px;
line-height: 12px;
float: left;
text-align: right;
cursor: pointer;
font-size: 12px;
}
dd ul li a{
display:block;
width: 26px;
height: 12px;
text-decoration: none;
color: #0000FF;
}

dd ul li a span{
display:none;
}

ul.days li a:hover{
color: #FBC500;
text-indent: 0; /*DO NOT REMOVE THIS OTHERWISE HOVER WILL NOT WORK IN IE6!*/
}

ul.days li a:hover span{
display: block;
position: absolute;
left: 5px;
top: 150px;
width: 200px;
height: 23px;
overflow: hidden;
text-align: center;
background: #5c68ba;
color: #FFFFFF;
text-decoration: none!important;
}
.month{
font-size: 12px;
font-weight: bold;
text-align: center;
}

.week li{
border: none;
font-weight: bold;
margin-left: 1px;
margin-right: 2px;
}

.days{
margin-left: 5px;
height: 98px;
*height: 85px;
width: 205px;
}

#descr{
clear: both;
color: #FFFFFF;
font-size: 12px;
text-align: center;
padding-top: 3px;
height: 23px;
}

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

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

Верстал я все это дело в XML/XSL, вот так выглядит тэмплэйт:

<dl>
<dt> </dt>
<dd>
<p class="month">
<xsl:value-of select="$SHARED/MONTHS/LIST[@ID= $month]/@NAME"/>
</p>
</dd>
<dd>
<ol class="week">
<xsl:for-each select="$SHARED/WEEK/ITEM">
<li><xsl:value-of select="text()"/></li>
</xsl:for-each>
</ol>
</dd>
<dd>
<ol class="days">
<xsl:for-each select="$SHARED/MONTHS/LIST[@ID= $month]/ITEM">
<li>
<a>
<xsl:if test="@NUM = $date">
<xsl:attribute name="class">currentday</xsl:attribute>
</xsl:if>
<xsl:attribute name="hrеf"><xsl:value-of select="@HREF"/></xsl:attribute>
<xsl:value-of select="@NUM"/>
<span><xsl:value-of select="text()"/></span>
</a>
</li>
</xsl:for-each>
</ol>
</dd>
<dd id="descr"><xsl:value-of select="$SHARED/MONTHS/DESCR"/></dd>
</dl>

На пути к XML нодам не обращайте внимания, особенности платформы.

28 thoughts on “Верстаем HTML/CSS календарь

  1. Круто. Респект и уважуха!
    Код стащил для дальнейшего (может, пригодится когда) использования. :)

  2. Супер!
    Вот только когда переставляешь 1е число и добавляются пустые элементы списка надо с них убирать ссылку, а то указатель над ними появляется.

  3. [quote post=”95″]так и не уловил смысла использования списка определений…[/quote]
    +1

    А вообще круто, спасибо огромное

  4. [quote comment=”409″]так и не уловил смысла использования списка определений…[/quote]

    Ну если подходить к вопросу с точки зрения семантики, то календарь это список определений – dl, месяц это термин/определенный период, соответственно dt, а дни это описание/значение для термина, выраженое через список, соответственно dd.

    Вроде логично, нет?

  5. [quote comment=”413″]Супер!
    Вот только когда переставляешь 1е число и добавляются пустые элементы списка надо с них убирать ссылку, а то указатель над ними появляется.[/quote]

    Да, верно подмечено.

  6. не зачет! календарь – есть колссические табличные данные и должен верстаться таблицей.

  7. [quote comment=”424″]не зачет! календарь – есть колссические табличные данные и должен верстаться таблицей.[/quote]

    С какой же это радости “календарь это классические табличные данные”? В данном случае календарь это список дней.

    [quote comment=”424″]а месяцем будет заголовок таблицы[/quote]

    А годом будет заголовок заголовка таблицы?

  8. [quote post=”95″]С какой же это радости “календарь это классические табличные данные”? В данном случае календарь это список дней.[/quote]

    Да с той самой, что календарь есть таблица с заголовком в виде месяца и днями недели в виде заголовков столбцов. Почитайте намного материала по семантике.

    [quote post=”95″]А годом будет заголовок заголовка таблицы?[/quote]

    А это уже не имеет значения, хоть в год возьмите.

  9. Всё-таки, zigzag, имхо, прав. Календарь – классический случай табличных данных, хотя бы потому, что исходные данные для календаря – множество состоящее из пар вида: (неделя, день недели).

  10. [quote post=”95″]исходные данные для календаря – множество состоящее из пар вида: (неделя, день недели)[/quote]

    Ну какая же это пара? По логике это список, подсписок.

  11. [quote comment=”446″]Вообще грят, что форич в xslt — зло.[/quote]

    Хм, интересно. Я xslt исключительно сам учил. Было бы интересно послушать что используют вместо.

  12. [quote comment=”492″]Тупо apply-templates.[/quote]

    Не, так беспонтово. Для создания списков вобще и менюшек в частности for-each – очень удобная штука.

  13. 2 Flack

    Леша, это оборот речи такой :) Согласен, не самый удачный. О каких понтах может идти речь если xsl-код никто кроме тебя не видит?
    Я собственно поэтому тебя и спросил в чем разница.
    То есть получается что apply-templates срабатывает быстрее чем for-each?

  14. меня научили так :)
    возможно, это особенность трансформатора, а возможно и такая же неписаная истина как и // в xpath.

  15. Календарь – таблица. Особенно в случае, когда есть названия дней – они так и просятся в th.

  16. [quote comment=”580″]Календарь – таблица. Особенно в случае, когда есть названия дней – они так и просятся в th.[/quote]

    Смотрите мои комментарии выше.

Leave a Reply

Your email address will not be published. Required fields are marked *