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

Давайте посмотрим как делается симпатичный календарь с помощью 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 нодам не обращайте внимания, особенности платформы.
Войти
RSS-подписка
Читать в ЖЖ
Яndex-Лента
В Google Reader
(проголосовали: 6, рейтинг: 4.67 из 5)
Круто. Респект и уважуха!
Код стащил для дальнейшего (может, пригодится когда) использования.
Комментарий by Jurij Burkanov — Май 16, 2007 @ 2:26 pm | Цитировать
2 Jurij Burkanov
спасибо
Комментарий by neutrino — Май 16, 2007 @ 2:52 pm | Цитировать
так и не уловил смысла использования списка определений…
Комментарий by LeshaOgonkov — Май 16, 2007 @ 4:02 pm | Цитировать
Супер!
Вот только когда переставляешь 1е число и добавляются пустые элементы списка надо с них убирать ссылку, а то указатель над ними появляется.
Комментарий by Виктор — Май 17, 2007 @ 9:59 pm | Цитировать
+1
А вообще круто, спасибо огромное
Комментарий by Gorik — Май 17, 2007 @ 10:08 pm | Цитировать
Ну если подходить к вопросу с точки зрения семантики, то календарь это список определений - dl, месяц это термин/определенный период, соответственно dt, а дни это описание/значение для термина, выраженое через список, соответственно dd.
Вроде логично, нет?
Комментарий by neutrino — Май 17, 2007 @ 10:38 pm | Цитировать
Да, верно подмечено.
Комментарий by neutrino — Май 17, 2007 @ 10:40 pm | Цитировать
не зачет! календарь - есть колссические табличные данные и должен верстаться таблицей.
Комментарий by Zigzag — Май 18, 2007 @ 10:18 pm | Цитировать
а месяцем будет заголовок таблицы
Комментарий by Zigzag — Май 18, 2007 @ 10:19 pm | Цитировать
С какой же это радости “календарь это классические табличные данные”? В данном случае календарь это список дней.
А годом будет заголовок заголовка таблицы?
Комментарий by neutrino — Май 19, 2007 @ 12:25 am | Цитировать
Да с той самой, что календарь есть таблица с заголовком в виде месяца и днями недели в виде заголовков столбцов. Почитайте намного материала по семантике.
А это уже не имеет значения, хоть в год возьмите.
Комментарий by Zigzag — Май 19, 2007 @ 9:23 am | Цитировать
Zigzag, ссылки в студию.
Комментарий by neutrino — Май 19, 2007 @ 11:05 am | Цитировать
Всё-таки, zigzag, имхо, прав. Календарь - классический случай табличных данных, хотя бы потому, что исходные данные для календаря - множество состоящее из пар вида: (неделя, день недели).
Комментарий by annoymouse — Май 22, 2007 @ 12:20 pm | Цитировать
Ну какая же это пара? По логике это список, подсписок.
Комментарий by neutrino — Май 22, 2007 @ 3:24 pm | Цитировать
Вообще грят, что форич в xslt — зло.
Комментарий by Flack — Май 22, 2007 @ 8:13 pm | Цитировать
Хм, интересно. Я xslt исключительно сам учил. Было бы интересно послушать что используют вместо.
Комментарий by neutrino — Май 22, 2007 @ 10:32 pm | Цитировать
Комментарий by rmaksim — Май 23, 2007 @ 1:06 am | Цитировать
Так же сделал в начале года на http://tvoibar.ru/ .
Списки это хорошая идея.
Комментарий by lusever — Май 27, 2007 @ 8:22 pm | Цитировать
Получилось неплохо
Комментарий by neutrino — Май 27, 2007 @ 10:38 pm | Цитировать
Тупо apply-templates.
Комментарий by Flack — Май 28, 2007 @ 7:54 am | Цитировать
Не, так беспонтово. Для создания списков вобще и менюшек в частности for-each - очень удобная штука.
Комментарий by neutrino — Май 28, 2007 @ 9:53 am | Цитировать
Речь о производительности, а не о понтах.
Комментарий by Flack — Май 28, 2007 @ 12:58 pm | Цитировать
2 Flack
Леша, это оборот речи такой
Согласен, не самый удачный. О каких понтах может идти речь если xsl-код никто кроме тебя не видит?
Я собственно поэтому тебя и спросил в чем разница.
То есть получается что apply-templates срабатывает быстрее чем for-each?
Комментарий by neutrino — Май 28, 2007 @ 2:15 pm | Цитировать
меня научили так
возможно, это особенность трансформатора, а возможно и такая же неписаная истина как и // в xpath.
Комментарий by Flack — Май 28, 2007 @ 7:45 pm | Цитировать
Календарь - таблица. Особенно в случае, когда есть названия дней - они так и просятся в th.
Комментарий by jahson — Июнь 7, 2007 @ 6:30 pm | Цитировать
Смотрите мои комментарии выше.
Комментарий by neutrino — Июнь 7, 2007 @ 9:04 pm | Цитировать
спасибо автору за статью. Очень полезно
Комментарий by Бассейныч — Июнь 18, 2007 @ 5:45 pm | Цитировать