Календарь на AJAX и PHP

Календарь является неотъемлемой частью дизайна многих сайтах. Часто он представляет собой один из многочисленных плагинов JQuery для календаря. Но он также может быть реализован с помощью PHP.

Сегодня я покажу вам, как создать помесячный календарь с возможностью прокрутки (стрелками вправо и влево) месяцев с использованием технологии AJAX.

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

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

Вадим Дворниковавтор материала

Предварительный просмотр

Предварительный просмотр

Демонстрация

Скачать источник

Структура папок

Для начала давайте четко определимся со структурой папок для всех файлов, которые мы будем создавать:

  • CSS - для всех файлов CSS;
  • изображения - для всех возможных изображений;
  • шаблоны - для всех файлов шаблонов.

Шаг 1. HTML

Я не собираюсь использовать какую-либо конкретную системы шаблонов (например, Smarty), я буду использовать только простые шаблоны HTML с собственными ключами.

Шаблоны / index.html:

<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="utf-8" />
	    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
	    <title>PHP AJAX Calendar</title>
	 
	    <!-- add styles and scripts -->
	    <link href="css/styles.css" rel="stylesheet" type="text/css" />
	    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>

	</head>
	<body>
	    <div id="calendar">
	        __calendar__
	    </div>
	</body>
	</html>

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

Второй шаблон будет использоваться в качестве внутреннего контейнера:

Шаблоны / calendar.html:

<div class="navigation">
	    <a class="prev" href="index.php?month=__prev_month__" onclick="$('#calendar').load('index.php?month=__prev_month__&_r=' + Math.random()); return false;"></a>
	    <div class="title" >__cal_caption__</div>
	    <a class="next" href="index.php?month=__next_month__" onclick="$('#calendar').load('index.php?month=__next_month__&_r=' + Math.random()); return false;"></a>
	</div>
	 
	<table>
	    <tr>
	        <th class="weekday">sun</th>
	        <th class="weekday">mon</th>
	        <th class="weekday">tue</th>
	        <th class="weekday">wed</th>
	        <th class="weekday">thu</th>
	        <th class="weekday">fri</th>
	        <th class="weekday">sat</th>
	    </tr>
	    __cal_rows__
	</table>

Используем мы его потому, что при Ajax-запросах вовсе не обязательно выводить все значения, достаточно только внутреннего содержимого календаря.

Шаг 2 . PHP

Пора действовать.

index.php

// Устанавливаем текущий год, месяц и день
list($iNowYear, $iNowMonth, $iNowDay) = explode('-', date('Y-m-d'));
 
// Устанавливаем текущий год, месяц в зависимости от возможных параметров GET 
if (isset($_GET['month'])) {
    list($iMonth, $iYear) = explode('-', $_GET['month']);
    $iMonth = (int)$iMonth;
    $iYear = (int)$iYear;
} else {
    list($iMonth, $iYear) = explode('-', date('n-Y'));
}
 
// Получаем названия и количество дней в конкретном месяце
$iTimestamp = mktime(0, 0, 0, $iMonth, $iNowDay, $iYear);
list($sMonthName, $iDaysInMonth) = explode('-', date('F-t', $iTimestamp));
 
// Получаем предыдущий год и месяц 
$iPrevYear = $iYear;
$iPrevMonth = $iMonth - 1;
if ($iPrevMonth <= 0) {
    $iPrevYear--;
    $iPrevMonth = 12; // set to December
}
 
// Получаем следующий год и месяц
$iNextYear = $iYear;
$iNextMonth = $iMonth + 1;
if ($iNextMonth > 12) {
    $iNextYear++;
    $iNextMonth = 1;
}
 
// Получаем количество дней в предыдущем месяце
$iPrevDaysInMonth = (int)date('t', mktime(0, 0, 0, $iPrevMonth, $iNowDay, $iPrevYear));
 
// Получаем числовое представление дней недели от первого дня конкретного (текущего) месяца.
$iFirstDayDow = (int)date('w', mktime(0, 0, 0, $iMonth, 1, $iYear));
 
// С этого дня начинается предыдущий месяц 
$iPrevShowFrom = $iPrevDaysInMonth - $iFirstDayDow + 1;
 
// Если предыдущий месяц
$bPreviousMonth = ($iFirstDayDow > 0);
 
// Тогда первый день
$iCurrentDay = ($bPreviousMonth) ? $iPrevShowFrom : 1;
 
$bNextMonth = false;
$sCalTblRows = '';
 
// Генерируем строки календаря
for ($i = 0; $i < 6; $i++) { // 6-weeks range
    $sCalTblRows .= '<tr>';
    for ($j = 0; $j < 7; $j++) { // 7 days a week
 
        $sClass = '';
        if ($iNowYear == $iYear && $iNowMonth == $iMonth && $iNowDay == $iCurrentDay && !$bPreviousMonth && !$bNextMonth) {
            $sClass = 'today';
        } elseif (!$bPreviousMonth && !$bNextMonth) {
            $sClass = 'current';
        }
        $sCalTblRows .= '<td class="'.$sClass.'"><a href="javascript: void(0)">'.$iCurrentDay.'</a></td>';
 
        // Следующий день
        $iCurrentDay++;
        if ($bPreviousMonth && $iCurrentDay > $iPrevDaysInMonth) {
            $bPreviousMonth = false;
            $iCurrentDay = 1;
        }
        if (!$bPreviousMonth && !$bNextMonth && $iCurrentDay > $iDaysInMonth) {
            $bNextMonth = true;
            $iCurrentDay = 1;
        }
    }
    $sCalTblRows .= '</tr>';
}
 
// Готовим замену ключей и генерируем календарь
$aKeys = array(
    '__prev_month__' => "{$iPrevMonth}-{$iPrevYear}",
    '__next_month__' => "{$iNextMonth}-{$iNextYear}",
    '__cal_caption__' => $sMonthName . ', ' . $iYear,
    '__cal_rows__' => $sCalTblRows,
);
$sCalendarItself = strtr(file_get_contents('templates/calendar.html'), $aKeys);
 
// AJAX-запрос – выводит календарь
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] =='XMLHttpRequest' && isset($_GET['month'])) {
    header('Content-Type: text/html; charset=utf-8');
    echo $sCalendarItself;
    exit;
}
 
$aVariables = array(
    '__calendar__' => $sCalendarItself,
);
echo strtr(file_get_contents('templates/index.html'), $aVariables);

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

Затем генерируем строки календаря (в днях), и, наконец, после этого заменяем ключи шаблона на собственные значения.

Через запрос Ajax мы выводим только внутреннее содержание ($sCalendarItself), в противном случае - мы показываем всю страницу.

Шаг 3. CSS

На данный момент, наш календарь не выглядит презентабельно, потому что это всего лишь голый HTML-код. Давайте украсим его.

CSS / styles.css

/* стили календаря */
#calendar {
    -moz-user-select: none;
    border: 1px solid #EEEEEE;
    border-radius: 6px 6px 6px 6px;
    color: #333333;
    font-family: Arial,sans-serif;
    font-size: 1.1em;
    margin: 10px auto;
    padding: 0.4em;
    width: 90%;
}
#calendar .navigation {
    background-color: #CC0000;
    border: 1px solid #E3A1A1;
    border-radius: 6px 6px 6px 6px;
    color: #FFFFFF;
    font-weight: bold;
    padding: 1px;
    position: relative;
}
#calendar .navigation .title {
    background: none repeat scroll 0 0 transparent;
    border-color: rgba(0, 0, 0, 0);
    color: inherit;
    line-height: 1.8em;
    margin: 0 2.3em;
    text-align: center;
}
#calendar .navigation .prev, #calendar .navigation .next {
    background-image: url(../images/nav.png);
    height: 24px;
    opacity: 0.9;
    position: absolute;
    top: 4px;
    width: 24px;
}
#calendar .navigation .prev {
    background-position: 0 0;
    left: 4px;
}
#calendar .navigation .next {
    background-position: -24px 0;
    right: 4px;
}
#calendar .navigation .prev:hover, #calendar .navigation .next:hover {
    opacity: 1;
}
#calendar table {
    border-collapse: collapse;
    font-size: 0.9em;
    table-layout: fixed;
    width: 100%;
}
#calendar table th {
    border: 0 none;
    font-weight: bold;
    padding: 0.7em 0.3em;
    text-align: center;
}
#calendar table td {
    border: 0 none;
    padding: 1px;
}
#calendar table td a {
    background-color: #EEEEEE;
    border: 1px solid #D8DCDF;
    color: #004276;
    display: block;
    font-weight: normal;
    opacity: 0.7;
    padding: 0.2em;
    text-align: right;
    text-decoration: none;
}
#calendar table td a:hover {
    background-color: #F6F6F6;
    border: 1px solid #CDD5DA;
    color: #111111;
}
#calendar table td.current a {
    font-weight: bold;
    opacity: 1;
}
#calendar table td.today a {
    background-color: #FBF8EE;
    border: 1px solid #FCD3A1;
    color: #444444;
    font-weight: bold;
    opacity: 1;
}

Шаг 4. Изображения

В стилях нашего календаря используется только одно небольшое изображение: nav.png

Демонстрация

Скачать источник

Заключение

Вот и все на сегодня, мы только что создали стильный адаптивный календарь.

Спасибо за Ваше внимание. Если вам действительно понравилось то, что мы сделали сегодня - поделитесь со своими друзьями ссылками используемыми ниже.

Вадим Дворниковавтор-переводчик статьи «PHP AJAX Calendar»

Комментарии

Оставьте свой комментарий
ДК
Дмитрий К.

У вас ошибка тут $iTimestamp = mktime(0, 0, 0, $iMonth, $iNowDay, $iYear);
Вместо $iNowDay нужно поставить 1. Иначе если в следующем или предыдущем месяце дней меньше чем в $iNowDay, то скрипт "перепрыгивает" через месяц, и к примеру, вместо сентября показывает октябрь

С
Сергей

А как сделать чтобы первый день был не Воскресенье, а Понедельник? Не пойму никак.

ДК
Дмитрий К.

после строки
$iFirstDayDow = (int)date('w', mktime(0, 0, 0, $iMonth, 1, $iYear));

добавьте:
$iFirstDayDow = $iFirstDayDow - 1;
if($iFirstDayDow == -1){
$iFirstDayDow = 6;
}

Д
Данияр

Классно!!!