Frontend Evolution #2: Як війна браузерів народила динамічний веб, DHTML революція та Microsoft vs Netscape битва за DOM (1995-2000)
🎯 Від статичних сторінок до інтерактивних додатків
Пам’ятаю, як у 2008 році, починавши кар’єру в одній з європейських компанії, я вперше натрапив на legacy проект з DHTML кодом кінця
У попередній статті ми розглянули, як 18 HTML-тегів заклали фундамент веба, але до середини
📚 Історичний контекст: коли статичність стала проблемою
Криза статичного веба
До 1997 року веб-розробка зводилася до створення статичних HTML-документів. Будь-яка інтерактивність означала повне перезавантаження сторінки — процес, що займав хвилини на повільних модемних з’єднаннях. Навіть проста валідація форми вимагала відправки даних на сервер через CGI-скрипти.
Бізнес вимагав більшого: миттєвої реакції на дії користувачів, динамічного оновлення контенту, складних інтерфейсів. Статичний HTML був як друкована книга — красиво, але абсолютно нерухомо.
Українські реалії: повільний Інтернет як каталізатор
В Україні кінця
В таких умовах будь-яка технологія, що мінімізувала серверні запити, була на вагу золота. Саме тому українські веб-студії активно експериментували з ранніми формами динамічності.
Війна браузерів як каталізатор інновацій
Ключові віхи протистояння:
- 1995 — JavaScript від Netscape vs JScript від Microsoft
- 1996 — CSS 1.0 з різними інтерпретаціями
- 1997 — рік народження DHTML: IE 4.0 vs Navigator 4.0
- 1998 — пік нововведень та критичної несумісності
Кожна компанія намагалася переманити розробників унікальними можливостями, що призвело до появи двох повністю несумісних підходів до динамічного веба.
⚙️ Технічна архітектура: два світи DHTML
Internet Explorer: революція document.all
Microsoft з IE 4.0 запропонувала радикальну ідею: кожен елемент HTML-документа має бути доступний для маніпуляцій через JavaScript. Це було реалізовано через революційну колекцію document.all
:
// IE 4.0+ - доступ до будь-якого елемента function changeContent() { // Доступ за ID document.all["myDiv"].innerHTML = "Новий контент!"; // Зміна стилів document.all["myDiv"].style.color = "red"; document.all["myDiv"].style.fontSize = "20px"; // Динамічна зміна позиції document.all["myDiv"].style.position = "absolute"; document.all["myDiv"].style.left = "100px"; document.all["myDiv"].style.top = "50px"; } // Революційна можливість - innerHTML function updatePage() { document.all["content"].innerHTML = "<h2>Динамічно створений заголовок</h2>" + "<p>Цей контент з'явився без перезавантаження!</p>"; }
Visual Filters: Photoshop у браузері
Microsoft пішов далі, інтегрувавши в IE 4.0 Visual Filters — технологію, що дозволяла застосовувати Photoshop-подібні ефекти до HTML-елементів:
// IE Visual Filters - справжня магія 1997 року! function applyEffects() { // Розмиття document.all["myDiv"].style.filter = "blur(strength=5)"; // Прозорість з градієнтом document.all["header"].style.filter = "alpha(opacity=50) gradient(startColorstr='#FF0000', endColorstr='#0000FF')"; // Тінь document.all["text"].style.filter = "dropshadow(color='black', offx=3, offy=3)"; // Світіння document.all["button"].style.filter = "glow(color='yellow', strength=3)"; }
Netscape Navigator: філософія Layers
Netscape обрала принципово інший підхід. Замість універсального доступу до всіх елементів, вони створили концепцію «слоїв» (Layers) — спеціальних контейнерів для динамічного контенту:
// Netscape 4.x - тільки абсолютно позиціоновані елементи function moveLayer() { // Доступ тільки до layer'ів document.layers["myLayer"].left = 200; document.layers["myLayer"].top = 100; document.layers["myLayer"].visibility = "show"; } // Детальніше про Netscape Layers API var myLayer = document.layers["gameLayer"]; // Позиціонування myLayer.moveTo(100, 200); // Абсолютна позиція myLayer.moveBy(50, -30); // Відносне переміщення myLayer.moveToAbsolute(300, 400); // Відносно всієї сторінки // Z-index маніпуляції myLayer.moveAbove(document.layers["background"]); myLayer.moveBelow(document.layers["foreground"]); // Розміри та відсікання myLayer.resizeTo(200, 150); myLayer.resizeBy(20, 10); myLayer.clip.top = 10; myLayer.clip.right = 190; // Динамічне завантаження контенту myLayer.src = "external_content.html"; myLayer.load("dynamic_page.html", 300); // з заданою шириною // Видимість myLayer.visibility = "hide"; // "show" | "hide" | "inherit"
JavaScript Style Sheets (JSS): альтернатива CSS
Окрім layers, Netscape запропонувала революційну альтернативу CSS — JavaScript Style Sheets (JSS). Замість декларативного CSS синтаксису, JSS дозволяв описувати стилі через JavaScript:
// JSS - JavaScript Style Sheets (тільки Navigator 4) tags.H1.color = "blue"; tags.H1.fontSize = "24pt"; tags.P.marginLeft = "20px"; // Умовні стилі if (navigator.appName == "Netscape") { tags.BODY.backgroundColor = "lightblue"; } else { tags.BODY.backgroundColor = "white"; } // Класи через JSS classes.highlight.P.backgroundColor = "yellow"; classes.highlight.P.fontWeight = "bold";
JSS виглядав потужно для програмістів, але мав фатальну ваду — підтримувався тільки Navigator 4 і ніколи не став стандартом.
VBScript: Microsoft альтернатива JavaScript
Microsoft також просувала VBScript як альтернативу JavaScript, особливо для корпоративних клієнтів:
' VBScript - тільки Internet Explorer Sub ChangeContent() Document.All("myDiv").innerHTML = "Оновлено через VBScript!" Document.All("myDiv").Style.Color = "red" End Sub ' Обробка подій Sub Button1_OnClick() MsgBox "Кнопка натиснута!" Document.All("result").innerHTML = "VBScript працює!" End Sub
Це створювало ще більшу фрагментацію — тепер розробники мали вибирати між JavaScript (кроссбраузерний) та VBScript (тільки IE, але інтегрований з Windows).
Архітектурні відмінності
Різниця була фундаментальною. Ось як виглядали ключові аспекти в кожному браузері:
🌐 Internet Explorer 4.0+
// ✅ Доступ до будь-якого елемента document.all["anyElement"].innerHTML = "Новий контент"; document.all["anyElement"].style.color = "red"; // ⚡ Швидка зміна контенту document.all["content"].innerHTML = "Миттєве оновлення"; // 🎨 Повний контроль CSS document.all["box"].style.fontSize = "20px"; document.all["box"].style.backgroundColor = "blue"; // 🌊 Event bubbling element.onclick = function() { /* подія спливає вгору */ }; // 🎭 Visual Filters (унікальна фішка IE) document.all["text"].style.filter = "glow(color='yellow', strength=3)";
📦 Netscape Navigator 4.x
// ❌ Доступ тільки до layers document.layers["myLayer"].left = 100; document.layers["myLayer"].visibility = "show"; // 🐌 Повільна зміна контенту var layer = document.layers["myLayer"]; layer.document.open(); layer.document.write("Треба відкрити/закрити"); layer.document.close(); // 🔒 Обмежені CSS можливості layer.left = 50; // ✅ Можна layer.visibility = "hide"; // ✅ Можна // layer.style.fontSize — ❌ Неможливо! // 🎯 Event capture тільки на layers layer.captureEvents(Event.MOUSEOVER); layer.onmouseover = handleEvent; // 💻 JavaScript Style Sheets (унікальна фішка Netscape) tags.H1.color = "blue"; tags.P.fontSize = "14pt";
🔍 Ключові філософські відмінності:
Internet Explorer: «Кожен HTML-елемент — це програмний об’єкт»
- Універсальний доступ до DOM
- Повна свобода маніпуляцій
Netscape Navigator: «Слоєва архітектура для позиціонованого контенту»
- Тільки спеціальні контейнери
- Фокус на позиціонуванні та анімації
💼 Практичні кейси: реальність розробки того часу
Проект: модернізація банківського порталу (2009)
Працюючи над оновленням банківського порталу для європейського клієнта, ми зіткнулися з класичним legacy кодом DHTML епохи. Система була написана в
Результатом був код-монстр з визначенням типу браузера:
// Визначення типу браузера - щоденна реальність 1999 року var isIE = document.all ? true : false; var isNetscape = document.layers ? true : false; function showBalance(amount) { if (isIE) { // IE версія document.all["balance"].innerHTML = "<strong>₴" + amount + "</strong>"; document.all["balance"].style.color = "green"; } else if (isNetscape) { // Netscape версія var layer = document.layers["balance"]; layer.document.open(); layer.document.write( "<font color='green'><b>₴" + amount + "</b></font>" ); layer.document.close(); } } // Dropdown меню - подвійна реалізація function toggleMenu(menuId) { if (isIE) { var menu = document.all[menuId]; menu.style.display = menu.style.display === "none" ? "block" : "none"; } else if (isNetscape) { var menu = document.layers[menuId]; menu.visibility = menu.visibility === "hide" ? "show" : "hide"; } }
Уроки, які я виніс з вивчення legacy коду:
- Подвійна розробка — кожну функцію доводилося писати двічі
- Визначення браузера як обов’язкова практика того часу
- Degradation strategy — сайт мав працювати навіть без JS
- User education — інструкції «Завантажте IE 4+» на старих сайтах
Цей досвід допоміг мені зрозуміти, наскільки важливі стандарти та кроссбраузерність у сучасній розробці.
Кейс: навчання через legacy код
Працюючи з різними CMS та legacy проектами, я часто натрапляв на коди DHTML епохи. Особливо цікавим був один Drupal-проект 2002 року з модулем динамічного меню, написаним у стилі тих часів:
// Реальний код з WordPress плагіна ~2003 року function wpMenuDHTML() { if (document.all) { // IE branch for(var i = 0; i < document.all.tags("LI").length; i++) { if(document.all.tags("LI")[i].className == "menu-item") { document.all.tags("LI")[i].onmouseover = function() { this.style.backgroundColor = "#cccccc"; if(this.children[1]) { this.children[1].style.display = "block"; } }; } } } else if (document.layers) { // Netscape branch (вже не актуальний у 2003, але залишався в коді!) for(var i = 0; i < document.layers.length; i++) { if(document.layers[i].id.indexOf("menu") > -1) { document.layers[i].captureEvents(Event.MOUSEOVER); document.layers[i].onmouseover = showSubmenu; } } } }
Досвід з legacy CMS та DHTML артефактами
Я регулярно натрапляв на різні подібні «артефакти»:
Magento 1.x legacy модулі:
// Реальний код з Magento модуля ~2004 року function MagentoProductTabs() { // Стара школа DHTML if (document.all) { // IE версія this.tabs = document.all.tags('DIV'); this.showTab = function(tabId) { for(var i = 0; i < this.tabs.length; i++) { if(this.tabs[i].className.indexOf('product-tab') > -1) { this.tabs[i].style.display = 'none'; } } document.all[tabId].style.display = 'block'; }; } else if (document.layers) { // Netscape - навіть у 2004! this.showTab = function(tabId) { for(var layerId in document.layers) { if(layerId.indexOf('tab') > -1) { document.layers[layerId].visibility = 'hide'; } } document.layers[tabId].visibility = 'show'; }; } }
Альтернативи DHTML: Flash та перші скрипти
Паралельно з DHTML розвивалася інша технологія — Macromedia Flash. Цікаво, що на початку Flash мав лише примітивні «Actions» (Flash Player 4, 1999), а повноцінний ActionScript 1.0 з’явився тільки в 2000 році з Flash Player 5:
// Flash Actions (1999) - примітивні команди gotoAndPlay(1); gotoAndStop(10); nextFrame(); // ActionScript 1.0 (2000) - вже справжня мова програмування function updateBalance(amount) { // Flash виконував цей код швидше за JavaScript того часу balanceText.text = "$" + amount; balanceText._alpha = 0; // Анімація через timeline for(var i = 0; i <= 100; i++) { balanceText._alpha = i; } }
Порівняння Flash vs DHTML (2000+ роки):
🌐 DHTML (з 1997 року)
- ⚡ Швидкість: Інтерпретований JavaScript — повільніше
- 🌍 Сумісність: Браузерні війни — код для IE ≠ код для Netscape
- 📦 Розмір: Легкі HTML/CSS/JS файли — швидке завантаження
- 🔍 SEO: Повністю індексується пошуковиками
- 💰 Вартість: Безкоштовно, відкриті стандарти
- 🛠️ Інструменти: Текстовий редактор, прості IDE
🎬 Flash ActionScript (з 2000 року)
- ⚡ Швидкість: Компільований байт-код — значно швидше
- 🌍 Сумісність: Однаковий код працює скрізь через Flash Player
- 📦 Розмір: «Важкі» SWF файли — повільне завантаження
- 🔍 SEO: Чорна скринька для пошуковиків — контент невидимий
- 💰 Вартість: Платні Adobe інструменти (Flash Professional)
- 🛠️ Інструменти: Спеціалізовані IDE, timeline editor
🎯 Практичні наслідки:
Коли обирали DHTML:
- SEO-критичні сайти (магазини, корпоративні)
- Швидке завантаження на повільному інтернеті
- Обмежений бюджет на інструменти
Коли обирали Flash:
- Ігри та мультимедійний контент
- Складна анімація та візуальні ефекти
- Одноразові рекламні банери
- Інтерактивні презентації та портфоліо
Ember.js як еволюція DHTML підходів
Пізніше, працюючи з Ember.js, я зрозумів, як концепції DHTML еволюціонували. Ember’s convention-over-configuration нагадував структурований підхід до організації DHTML коду:
// Ember.js - еволюція DHTML організації App.MenuComponent = Ember.Component.extend({ // Lifecycle methods - еволюція DHTML init/destroy didInsertElement: function() { this.initDynamicBehavior(); }, willDestroyElement: function() { this.cleanup(); // Memory management як в DHTML }, actions: { toggleSubmenu: function() { // Event handling - прямий спадкоємець DHTML events this.toggleProperty('isVisible'); } } });
🌱 Спадок та вплив на сучасну розробку
Архітектурні паттерни DHTML епохи та їх спадок
DHTML епоха породила фундаментальні архітектурні підходи, які живуть досі:
1. Component-based Architecture
// DHTML підхід - 1998 рік function MenuComponent(containerId) { this.container = document.all ? document.all[containerId] : document.layers[containerId]; this.show = function() { /* логіка показу */ }; this.hide = function() { /* логіка приховування */ }; this.destroy = function() { /* cleanup */ }; } // Сучасний React - 2025 рік function MenuComponent({ isVisible, onClose }) { return isVisible ? <div>Menu content</div> : null; }
2. State Management Pattern
// DHTML State Manager - прототип Redux var AppState = { currentMenu: null, userLoggedIn: false, setState: function(newState) { // Примітивний state update for(var prop in newState) { this[prop] = newState[prop]; } this.render(); }, render: function() { // Перерендер UI на основі state if(this.userLoggedIn) { showUserMenu(); } else { showLoginForm(); } } };
3. Event-Driven Architecture
// DHTML Events - народження паттерну function EventManager() { this.listeners = {}; this.on = function(event, callback) { if(!this.listeners[event]) { this.listeners[event] = []; } this.listeners[event].push(callback); }; this.emit = function(event, data) { if(this.listeners[event]) { for(var i = 0; i < this.listeners[event].length; i++) { this.listeners[event][i](data); } } }; } // Сучасний EventEmitter - той самий принцип!
4. Dependency Injection зародки
// DHTML "DI Container" - 1999 рік var ServiceContainer = { services: {}, register: function(name, factory) { this.services[name] = factory; }, get: function(name) { return this.services[name](); } }; // Реєстрація сервісів ServiceContainer.register('httpService', function() { return new XMLHttpRequest(); // коли з'явився! });
Performance-first thinking — DHTML змусив розробників думати про перформанс з самого початку:
// DHTML підхід - мінімізація DOM операцій var fragment = document.createDocumentFragment(); for(var i = 0; i < items.length; i++) { var li = document.createElement('li'); li.innerHTML = items[i]; fragment.appendChild(li); // Не торкаємось DOM! } list.appendChild(fragment); // Одна DOM операція // React Virtual DOM - та сама ідея, але краще function ItemList({ items }) { return ( <ul> {items.map(item => <li key={item.id}>{item.text}</li>)} </ul> ); // React оптимізує DOM операції }
Graceful Degradation принцип:
// DHTML підхід if(document.all || document.layers) { // Розширена функціональність initDynamicMenu(); } else { // Базова функціональність showStaticMenu(); } // Сучасний підхід if('IntersectionObserver' in window) { // Прогресивна функціональність initLazyLoading(); } else { // Fallback loadAllImages(); }
Cross-platform compatibility thinking:
// DHTML legacy function createElement(tag, props) { var element; if(document.createElement) { element = document.createElement(tag); } else { // IE5 fallback element = document.all.tags(tag)[0].cloneNode(false); } return element; } // Сучасні polyfills - та сама ідея if(!Array.prototype.includes) { Array.prototype.includes = function(searchElement) { return this.indexOf(searchElement) !== -1; }; }
Вплив на сучасні архітектури
Micro-frontends — прямий спадкоємець DHTML багатофайлової архітектури:
- DHTML: різні
.js
файли для різних частин сайту - Зараз: різні бандли для різних частин додатку
Component lifecycle — концепція з DHTML:
- DHTML:
init()
,show()
,hide()
,destroy()
- React:
componentDidMount()
,componentWillUnmount()
, etc.
Framework-agnostic thinking — DHTML навчив писати код, що працює скрізь:
// DHTML wrapper - прообраз jQuery var DOM = { get: function(id) { return document.all ? document.all[id] : document.getElementById(id); }, addClass: function(element, className) { if(element.className) { element.className += ' ' + className; } else { element.className = className; } } }; // jQuery - еволюція тієї ж ідеї $('#element').addClass('active');
Performance Lessons: від DHTML до сучасних SPA
Робота з trading системами навчила мене, що принципи оптимізації DHTML епохи актуальні досі:
// DHTML "батчинг" DOM операцій - 1998 function updateMultipleElements(updates) { // Збираємо всі зміни var changes = []; for(var i = 0; i < updates.length; i++) { changes.push({ element: document.all[updates[i].id], property: updates[i].property, value: updates[i].value }); } // Застосовуємо за один прохід for(var j = 0; j < changes.length; j++) { changes[j].element[changes[j].property] = changes[j].value; } } // React Concurrent Features - та сама ідея function TradingWidget({ data }) { return ( <React.Fragment> {data.map(item => ( <PriceRow key={item.id} price={item.price} /> ))} </React.Fragment> ); // React автоматично батчить оновлення }
Memory Management — критично важливо було в DHTML:
// DHTML cleanup pattern function destroyWidget() { // Видаляємо event listeners element.onclick = null; element.onmouseover = null; // Очищаємо посилання element.parentNode.removeChild(element); element = null; // Garbage collection hint if(window.CollectGarbage) { window.CollectGarbage(); // IE specific } }
Ці паттерни не стали основою для React’s useEffect
cleanup та Vue’s beforeDestroy
lifecycle методів. Але схожі проблеми призвели до схожих рішень.
Cross-browser compatibility — урок DHTML епохи:
- Тоді: подвійний код для IE/Netscape з перевіркою типу браузера
- Зараз: Babel, polyfills, autoprefixer
Але DHTML мав критичні обмеження: відсутність стандартизації призводила до фрагментації веба, а пряма маніпуляція DOM була повільною та неефективною для складних інтерфейсів. До 2000 року стало зрозуміло, що потрібні стандарти. Ці проблеми стали каталізатором для наступної революції — появи W3C DOM стандартів, CSS 2.1, стандартизації ECMAScript та народження концепції XMLHttpRequest (яка трохи пізніше переросте в Ajax), що дозволила створювати справді динамічні веб-додатки без постійних перезавантажень сторінок.
🎯 Висновки та перспективи
DHTML епоха
✅ Народження інтерактивності — революційний перехід від статичних документів до динамічних додатків
✅ DOM як програмна концепція — кожен елемент сторінки вперше став програмним об’єктом
✅ Event-driven архітектура — реакція на дії користувача без перезавантаження (прообраз сучасних SPA)
✅ Performance-first thinking — DHTML змусив думати про оптимізацію з першого дня
✅ Component paradigm — від Netscape layers до React компонентів
✅ Cross-platform compatibility — зародження підходів до кроссбраузерної розробки
Архітектурні уроки для сучасності:
- State management — централізоване управління станом додатку
- Lifecycle patterns — init/show/hide/destroy → mount/update/unmount
- Dependency injection — раннє розуміння важливості DI контейнерів
- Memory management — культура cleanup’у та уникнення memory leaks
- Progressive enhancement — graceful degradation як основа accessibility
Ключовий takeaway: технологічна конкуренція може стимулювати революційні інновації, але відсутність стандартів створює неприйнятну фрагментацію екосистеми. DHTML показав потужність динамічного веба та заклав архітектурні основи сучасних фреймворків, але також продемонстрував критичну важливість галузевого консенсусу в технологічних рішеннях.
Український контекст: В умовах повільного та дорогого Інтернету
Війна браузерів закінчилася перемогою стандартів: W3C DOM, CSS 2.1 та ECMAScript створили фундамент для наступного етапу. Про те, як ці стандарти привели до народження XMLHttpRequest/Ajax, появи перших JavaScript-бібліотек (Prototype, jQuery) та формування екосистеми, яка зробила веб-розробку знову передбачуваною та масштабованою, читайте в наступній статті циклу.
Корисні ресурси:
- W3C DOM History — офіційна історія стандартизації DOM
- MDN: Introduction to the DOM — еволюція від DHTML до сучасного DOM
Питання для обговорення: Чи стикалися ви з legacy кодом DHTML епохи? Які архітектурні паттерни того часу ви помічаєте в сучасних фреймворках? Як досвід повільного Інтернету
Немає коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів