К 2001 году казнет вырос. Алматинские компании уже понимали разницу между сайтом-визиткой и профессиональным веб-присутствием. Одним из маркеров «серьёзного» сайта стало выпадающее навигационное меню - наведи курсор на раздел, и подразделы появляются сами, без перезагрузки.
В казахстанских веб-студиях DHTML (Dynamic HTML) осваивали по статьям на JavaScript.ru, Forum.Web.de и книгам Дмитрия Котерова. Реализовать меню, которое работало одновременно в IE5 и Netscape 6, было задачей не тривиальной.
Как это работало: абсолютное позиционирование + display:none
Принцип: подменю - это обычные <div> с position: absolute и display: none. JavaScript показывал их при onmouseover на родительской ссылке и скрывал при onmouseout. Сложность: между ссылкой и подменю - пустой пиксель, который вызывал onmouseout до того, как мышь попадала в меню.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Навигация DHTML - Алматы, 2001</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<style type="text/css">
body { font-family: Arial, Helvetica, sans-serif; font-size: 13px;
margin: 0; padding: 0; }
/* Навигационная панель */
#nav {
width: 100%;
background-color: #003399;
height: 30px;
position: relative;
}
.nav-item { position: relative; float: left; }
.nav-item > a {
display: block;
padding: 6px 16px;
color: #ffffff;
text-decoration: none;
font-weight: bold;
font-size: 12px;
height: 18px;
line-height: 18px;
}
.nav-item > a:hover { background-color: #0055CC; }
/* Подменю */
.sub {
position: absolute;
top: 30px;
left: 0;
width: 180px;
background-color: #FFFFFF;
border: 1px solid #003399;
border-top: 3px solid #003399;
display: none;
z-index: 200;
}
.sub a {
display: block;
padding: 6px 12px;
color: #333333;
text-decoration: none;
font-size: 12px;
border-bottom: 1px dotted #cccccc;
}
.sub a:hover { background-color: #EEF3FF; color: #003399; }
/* Фиксируем высоту страницы для демонстрации */
.content { padding: 20px; }
</style>
</head>
<body>
<div id="nav">
<div class="nav-item">
<a href="about.asp"
onmouseover="openSub('sub-about')"
onmouseout="closeSub('sub-about')">О компании ▼</a>
<div class="sub" id="sub-about"
onmouseover="keepSub('sub-about')"
onmouseout="closeSub('sub-about')">
<a href="about/history.asp">История компании</a>
<a href="about/team.asp">Руководство</a>
<a href="about/partners.asp">Партнёры</a>
<a href="about/licenses.asp">Лицензии</a>
</div>
</div>
<div class="nav-item">
<a href="services.asp"
onmouseover="openSub('sub-services')"
onmouseout="closeSub('sub-services')">Услуги ▼</a>
<div class="sub" id="sub-services"
onmouseover="keepSub('sub-services')"
onmouseout="closeSub('sub-services')">
<a href="services/audit.asp">Аудит</a>
<a href="services/consulting.asp">Консалтинг</a>
<a href="services/outsourcing.asp">Аутсорсинг</a>
</div>
</div>
<div class="nav-item">
<a href="contacts.asp"
onmouseover="openSub('sub-contacts')"
onmouseout="closeSub('sub-contacts')">Контакты ▼</a>
<div class="sub" id="sub-contacts"
onmouseover="keepSub('sub-contacts')"
onmouseout="closeSub('sub-contacts')">
<a href="contacts/almaty.asp">Алматы (гл. офис)</a>
<a href="contacts/astana.asp">Астана</a>
<a href="contacts/form.asp">Написать нам</a>
</div>
</div>
</div>
<div class="content">
<h2>Добро пожаловать</h2>
<p>Наведите курсор на пункты меню выше.</p>
<!-- Тестовый select для проверки z-index проблемы IE5 -->
<select>
<option>Тест z-index (IE5 баг)</option>
<option>Астана</option>
<option>Алматы</option>
</select>
</div>
<script type="text/javascript">
// DHTML-меню для казнета, 2001 год
// Тестировалось в IE 5.0/5.5 и Netscape 6.1
var timers = {}; // таймеры задержки закрытия
// Открыть подменю
function openSub(id) {
closeAll();
var el = document.getElementById(id);
if (el) el.style.display = 'block';
cancelClose(id);
// Создать iframe-shim для IE5 (z-index поверх select)
createShim(el);
}
// Держать подменю открытым (мышь вошла в него)
function keepSub(id) {
cancelClose(id);
}
// Запланировать закрытие с задержкой
function closeSub(id) {
if (timers[id]) clearTimeout(timers[id]);
timers[id] = setTimeout(function() {
var el = document.getElementById(id);
if (el) {
el.style.display = 'none';
removeShim(el);
}
timers[id] = null;
}, 300);
// 300 мс - протестировано на казахстанских офисных машинах 2001 года.
// Трекболы и старые мыши требовали не меньше 250-300 мс.
}
function cancelClose(id) {
if (timers[id]) { clearTimeout(timers[id]); timers[id] = null; }
}
function closeAll() {
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
if (divs[i].className === 'sub') {
divs[i].style.display = 'none';
}
}
}
// ---- IE5 iframe shim ----
// Без него подменю уходит ПОД select-элементы в IE5/IE6
// В Netscape 6 эта проблема отсутствует
var shims = {};
function createShim(menuEl) {
if (!menuEl) return;
if (!document.createElement) return; // старые браузеры - пропустить
var shimId = 'shim_' + menuEl.id;
if (shims[shimId]) {
shims[shimId].style.display = 'block';
return;
}
var shim = document.createElement('iframe');
shim.id = shimId;
shim.src = 'about:blank';
shim.scrolling = 'no';
shim.frameBorder = '0';
shim.style.position = 'absolute';
shim.style.border = 'none';
shim.style.zIndex = '199'; // под меню (200), над select
// Позиционируем и размещаем поверх того же места
shim.style.left = menuEl.style.left || '0px';
shim.style.top = menuEl.style.top || '30px';
shim.style.width = '180px';
shim.style.height = '150px'; // примерная высота подменю
// Прозрачность для IE
if (shim.style.filter !== undefined) {
shim.style.filter = 'alpha(opacity=0)';
}
menuEl.parentNode.insertBefore(shim, menuEl);
shims[shimId] = shim;
}
function removeShim(menuEl) {
if (!menuEl) return;
var shimId = 'shim_' + menuEl.id;
if (shims[shimId]) {
shims[shimId].style.display = 'none';
}
}
// Закрывать меню при клике в любом месте страницы
document.onclick = function() { closeAll(); };
</script>
</body>
</html>
Проблемы с z-index: специфика IE5 в казнете
В IE5 абсолютно позиционированные блоки всегда отображались ПОД элементами <select>. Это был баг именно Microsoft IE - в Netscape 6 всё работало правильно. Но аудитория казнета в 2001 году использовала IE5 как основной браузер.
// Диагностика проблемы в консоли:
// Если меню уходит под select - нужен iframe shim
// Простой тест: есть ли в IE активный z-index баг?
var isIE = document.all && !window.opera;
// document.all - IE-специфичный объект
// window.opera - Netscape/Opera наличие
if (isIE) {
// В IE5/6 применяем шим
// В Netscape 6 - z-index работает корректно
}
Именно из-за этого бага казахстанские разработчики 2001 года нередко делали сайты без <select> на страницах с навигационным меню - или ставили <select> в нижней части страницы вдали от навбара.
DHTML и кодировка windows-1251
Одна тонкость: текст в JavaScript-строках через DHTML должен был быть в той же кодировке, что и страница. Для казнета 2001 года - windows-1251:
<!-- Страница в windows-1251 -->
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<script type="text/javascript">
// Строки в коде тоже windows-1251 - кириллица работает
var menuLabels = ['О компании', 'Услуги', 'Контакты'];
// showStatus() с кириллицей тоже работал в IE5 windows-1251
</script>
Если JavaScript-файл был в KOI8-R, а страница в windows-1251 - кириллица в статусной строке и тултипах превращалась в кракозябры. Проблема, которую начинающие разработчики в Алматы 2001 года встречали регулярно.
Наследие DHTML в современной казахстанской веб-разработке
К 2005 году jQuery убрал необходимость писать этот код вручную. К 2010 году - CSS3-переходы, :hover на блочных элементах и чистый CSS-дропдаун стали стандартом. К 2020 году - React-компоненты.
Но паттерн не изменился: setTimeout на 300 мс перед закрытием меню используют современные библиотеки UI-компонентов. Проблема z-index исчезла в браузерах примерно в 2007-2008 году - сразу после выхода IE7, исправившего этот баг. Инлайновые обработчики событий уступили место addEventListener. Суть осталась прежней.
Те, кто писал DHTML в казнете в 2001 году, получили фундаментальное понимание того, как браузер рендерит DOM и обрабатывает события. Этот фундамент не устарел - он остаётся основой фронтенд-разработки в 2026 году.