tl;dr Я бы сказал, что Microsoft на несколько световых лет опередила всех в разработке инструментов для проектирования сложных веб-сайтов. Сейчас эти технологии изобретают заново на руинах погибшей цивилизации.



Когда я был ребёнком, меня всегда завораживали истории о древних цивилизациях. Я зачитывался книгами об Атлантиде, об истории открытия Трои Генрихом Шлиманом, о греках, римлянах, империи инков и Древнем Египте. И меня всегда восхищали их продвинутые знания в области астрономии, математики и медицины, их невероятные достижения, возведение этих огромных монументов и построение высокофункциональных социальных систем. Что ещё более невероятно, так это то, что всё это было сделано за тысячи лет до появления христианской культуры!

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

Эпоха движка Trident


Теперь для миллионов пользователей Windows 10 компания Microsoft выпускает новый браузер Edge на основе Chromium. И это означает конец целой эпохи, которая прошла под знаком движка Trident.

Но разве эра Trident уже не закончилась, когда появился Edge? Не совсем.

Когда Microsoft создала браузер Edge в 2015 году, в реальности она просто форкнула Trident в EdgeHTML и почистила множество устаревших путей кода, таких как ActiveX (альтернатива Java-апплетам от Microsoft) или эмуляция старых движков рендеринга IE. Родственность двух браузеров очевидна, если почитать статьи самих разработчиков или по баг-репортам, когда одни и те же уязвимости одинаково влияют на IE 11 и Edge 17. На самом деле большинство первоначальных улучшений Edge получено от Chakra, движка JavaScript, и мало что можно отнести к самому движку рендеринга. Переименование браузера можно рассматривать скорее как маркетинговый ход, поскольку удаление устаревших функций уже началось раньше, когда браузер ещё назывался Internet Explorer.

Перезагрузка Internet Explorer под новым названием не вернула сердца веб-разработчиков. До сегодняшнего дня Microsoft занята игрой в догонялки. Поэтому, когда мы сегодня волнуемся о веб-платформе, это происходит не из-за нового выпуска Edge, а из-за того, что Google представляет новые идеи или API во время Google I/O или саммита Chrome Dev. Многие из этих инноваций приходят от других команд в Google, работающих на таких платформах Google, как Angular и AMP, или на продуктах Google, таких как Gmail, Search, Drive, Maps, Google Docs, Analytics или, в последнее время, Lighthouse. На самом деле, многое из того, что определяет HTML5, связано с тем, что Google ищет способ улучшить веб-платформу, чтобы лучше применить свои идеи в веб-приложениях. Помните Google Gears? Или потом Google Chrome Frame?

Забавно, что в старые времена тот же процесс стимулировал инновации в Internet Explorer. Возможности ActiveX добавили в Internet Explorer 3.0 вместе с тегом <object>, добавив ещё одну «цель компиляции» для конкурента Java. Конечно, идея исходила не от команды IE. Или возьмём то, что мы сегодня знаем как AJAX: идея ленивой выборки контента в фоновом режиме с помощью JavaScript родилась в команде Exchange / Outlook Web Access, этот продукт можно рассматривать как предшественника Gmail. Произведя несколько трюков на серверах Microsoft, они тихо выкатили компонент вместе с Internet Explorer 5.0 в 1999 году. Только через шесть лет был изобретён термин AJAX и его концепции стали широко известны.

«Мы довольно быстро договорились выпустить это как часть библиотеки MSXML. Вот откуда взялось название XMLHTTP — компонент в основном работает по HTTP и не имеет конкретной связи с XML, но так проще всего было вытолкнуть его в прод, так что нам пришлось втиснуть XML в название (к тому же, XML была модной технологией в то время, и это казалось хорошим маркетингом для компонента)».

То же самое касается и document.designMode (видимо, пожелание исходило от команды Hotmail) и contentEditable, DOM, Drag-n-Drop API, iframе и доступа к буферу обмена. Internet Explorer также стал первым браузером, который научился менять разрешения на лету через всплывающее окошко с запросом:



В те дни Microsoft в одиночку двигала вперёд интернет. Над Internet Explorer работало около тысячи (!) человек с бюджетом 100 миллионов долларов в год. С ними почти никто не мог конкурировать. Это было грандиозно!

«[Скотт] Айзек также изобрел HTML-тег iframe. Было высказано предположение, что название означает «фрейм Айзека», хотя Скотт отрицал это».

В 2012 году Internet Explorer последний раз представил новые функции по запросу других бизнес-подразделений. В то время для операционной системы Windows 8 сделали Windows Store, и появились соответствующие приложения Windows Store Apps. Однажды написанные, эти приложения потом запускаются на Windows, Xbox и Windows Phone. Поскольку Microsoft опоздала с каталогом приложений, то должна была максимально снизить барьер входа для разработчиков. Поэтому появилась идея разрешить в каталоге веб-приложения. В качестве прокси между веб-стеком и операционной системой была создана JavaScript- библиотека под названием WinJS, а средой выполнения для этих приложений стал Internet Explorer 10.



Но чтобы смоделировать Windows UI с помощью веб-технологий, Microsoft пришлось добавить в IE множество новых возможностей: CSS Grid, CSS Flexbox, CSS Scroll Snap Points и Pointer Events API для взаимодействия с сенсорным экраном и стилусом (последнее было необходимо, поскольку Apple подала патент на Touch API).


Microsoft даже изобрела то, что позже стало Origin Trials. Об этом рассказывается в подкасте, которое мы записали с Джейкобом Росси из команды Edge в 2015 году.

Возвращаясь к моим рассуждениям о древних цивилизациях и их достижениях, мне кажется, что Internet Explorer представил многие технологии, которые впоследствии были реализованы заново и которые мы теперь отмечаем как инновации. Хотя наши современные переосмысления предлагают больше возможностей и удобств, меня удивляет, почему сообщество разработчиков выбрало только очень немногие из них. Упомянутые выше инновации были подхвачены либо потому, что браузеры стремились к совместимости с IE, либо потому, что Microsoft оказалась в нужное время в нужном месте. Но их было гораздо больше!

Забытые технологии


MHTML


Первый в списке — MHTML или «MIME-инкапсуляция агрегированных HTML-документов». MHTML задумывался как формат упаковки контента, а концепция во многом повторяла способ добавления вложений в электронной почте. MHTML берёт HTML-файл, а в дополнительные разделы встраивает все необходимые ресурсы, такие как CSS, JavaScript-файлы и изображения, в кодировке base64. Это как data URI на стероидах. Можете считать MHTML предшественником Web Bundles. Формат поддерживался начиная с IE 5, а также в Opera на движке Presto. Ни один другой браузер официально не поддерживал MHTML, но Chromium позже добавил эту функцию, за флагом chrome://flags/#save-page-as-mhtml. MHTML предложили в качестве открытого стандарта IETF, но он почему-то не взлетел.

Интересный факт: Outlook Express использует MHTML внутри файлов .eml для хранения писем вместе с соответствующими вложениями на диске.

Спецэффекты между страницами


В Internet Explorer были фильтры перехода между страницами, которые можно определить как заголовок HTTP или в виде метатега:

<meta http-equiv="Page-Enter"
      content="RevealTrans(Duration=0.600, Transition=6)">

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

  • 0 — Box in
  • 1 — Box out
  • 2 — Circle in
  • 3 — Circle out
  • 4 — Wipe up
  • 5 — Wipe down
  • 6 — Wipe right
  • 7 — Wipe left
  • 8 — Vertical blinds
  • 9 — Horizontal blinds
  • 10 — Checkerboard across
  • 11 — Checkerboard down
  • 12 — Random dissolve
  • 13 — Split vertical in
  • 14 — Split vertical out
  • 15 — Split horizontal in
  • 16 — Split horizontal out
  • 17 — Strips left down
  • 18 — Strips left up
  • 19 — Strips right down
  • 20 — Strips right up
  • 21 — Random bars horizontal
  • 22 — Random bars vertical
  • 23 — Случайный фильтр из списка

Фильтр можно активировать на ввод страницы Page-Enter, а также установить дополнительные спецэффекты на Page-Exit, Site-Enter и Site-Exit. Эти мягкие переходы между страницами — то, что предлагают в новом формате порталов.

Спецэффекты между объектами


Аналогично переходу между страницами, можно использовать фильтры для перехода между двумя состояниями одного и того же объекта DOM. Это похоже на ramjet Рича Харриса, только он не трансформируется между двумя состояниями, а вместо этого смешивается в киношном стиле.

По своему эффекту эти фильтры похожи на CSS Transitions или анимированную функцию CSS crossfade().

Сначала применяем к элементу фильтр смешивания (длительностью 600 мс):

img.style.filter = 'blendTrans(duration=0.600)';

Затем, прежде чем вносить какие-либо изменения в объект, замораживаем его текущее состояние:

img.filters.blendTrans.apply();

Наконец, изменяем источник изображения и вызываем переход:

img.src = 'different-src.jpg';
img.filters.blendTrans.play();



Фильтры эффектов


Многие помнят эту категорию фильтров ещё со времён Internet Explorer 4+. В 1997 году они уже предлагали функциональность, близкую к CSS Filters, когда те впервые появились в Apple Safari 6 в 2012 году.



Matrix Filter из Internet Explorer можно применять для выполнения действий, которые позже будут представлены как часть функциональности CSS Transforms:

transform: rotate(15deg);
filter: progid:DXImageTransform.Microsoft.Matrix(
            M11=0.9659258262890683,
            M12=-0.2588190451025207,
            M21=0.2588190451025207,
            M22=0.9659258262890683,
            SizingMethod='auto expand');

Или можете использовать Chroma Filter для выделения пикселей определённого цвета, чтобы создать закруглённые углы или применить маску.

Градиентный фильтр


Вы думали, что поддержка градиентов впервые появилась в Internet Explorer 10? Это не совсем так, потому что для градиентов тоже был CSS-фильтр:

filter: progid:DXImageTransform.Microsoft.gradient(enabled='false',
                startColorstr=#550000FF, endColorstr=#55FFFF00)

Также обратите внимание, что Internet Explorer уже поддерживал 8-значные hex-коды для цветов RGBA, которые официально появились в CSS только в 2016 году как часть CSS Color Module Level 4.

Стилизация полосы прокрутки


Internet Explorer внедрил стилизацию полосы прокрутки ещё в 1999 году, и только в 2013 году разработчики Safari придумали собственную механику.



body {
  scrollbar-base-color: #C0C0C0;
  scrollbar-3dlight-color: #C0C0C0;
  scrollbar-highlight-color: #C0C0C0;
  scrollbar-track-color: #EBEBEB;
  scrollbar-arrow-color: black;
  scrollbar-shadow-color: #C0C0C0;
  scrollbar-darkshadow-color: #C0C0C0;
}

Box-Sizing


Internet Explorer изначально реализовал эту модель так, словно box-sizing: border-box установлен по умолчанию. Хотя многим подход Microsoft показался более логичным и удобным, но CSS WG в конечном счёте выбрала другое значение по умолчанию, где width относится не к внешней ширине блока, а к ширине контента внутри.



«Логично, что блок измеряется от края до края. Возьмите физическую коробку, любую коробку. Положите внутрь какой-нибудь предмет, который значительно меньше коробки. Попросите любого измерить ширину коробки. Он измерит расстояние между краями коробки («границы»). Никому и в голову не придёт измерять содержимое внутри. Веб-дизайнеры, создающие блоки для контента, заботятся о видимой ширине блока, о расстоянии от границы до границы. Именно границы, а не контент, являются визуальными сигналами для пользователей. Никого не интересует ширина контента».

Только в IE 6 добавили дополнительный режим рендеринга, на этот раз совместимый со стандартом. Он не был включен по умолчанию, чтобы не испортить старые макеты. Его нужно было специально выбрать объявлением doctype в начале HTML-документа (аналог 'use strict'; в JavaScript).

Сегодня все возвращаются обратно к модели IE, переопределяя термины в самом начале CSS:

html {
  box-sizing: border-box;
}

*, *::before, *::after {
  box-sizing: inherit;
}

CSS Expressions


До седьмой версии у Internet Explorer была отличная функция под названием CSS Expressions, выражения CSS, также известные как «динамические свойства». По сути, это были фрагменты JavaScript, завёрнутые в функцию CSS, а результат вычисления в этой функции становился значением свойства CSS. Их можно рассматривать как один из предшественников CSS Houdini и функции calc() и других функций.

Например, можете написать собственные функции min() и max():

width: expression(Math.min(this.parentElement.clientWidth, 400) + 'px');

Приведённый выше код устанавливает ширину элемента равной ширине родителя до тех пор, пока она не превысит 400px. На этом она остановится, примерно как действует max-width. Ключевое слово this ссылается на текущий элемент.

Поскольку IE поддерживает псевдоэлементы только с восьмой версии, вы можете использовать выражения CSS для их «полифиллинга», например:

zoom: expression(
    this.runtimeStyle.zoom = '1',
    this.insertBefore(document.createElement('span'),(this.hasChildNodes()
                        ? this.childNodes[0]
                        : null)).className='before',
    this.appendChild(document.createElement('span')).className='after'
  );

Приведённый код присваивается относительно постороннему CSS-свойству zoom. Первым делом он отключает дальнейшее выполнение выражения, заменив его статическим значением 1. Это предотвращает создание всё новых и новых элементов с каждым новым запуском. Затем создаёт элементы <span>, которые вводит в начале и в конце своей области содержимого, с именами классов .before и .after. Internet Explorer 8 был первой версией, которая поддерживала псевдоэлементы, но в то же время отказалась от поддержки CSS-выражений, поэтому приведённый код не повредит браузерам с псевдоэлементами.

От CSS-выражений в IE практически сразу отказались, потому что они быстро приобрели плохую репутацию. Всё дело в том, что с помощью CSS-выражений очень легко нагрузить CPU и снизить скорость рендеринга. «Проблема» с выражениями CSS заключалась в том, что они выполнялись после каждого события, зарегистрированного браузером, включая изменение размера, прокрутку и перемещение мыши. Посмотрите на следующий пример:

background: expression('#'+Math.floor(Math.random()*16777216).toString(16));

Этот код вычисляет рандомный цвет, который затем ставится фоном. Можете увидеть его в действии на следующем видео (предупреждение об эпилепсии из-за мигающих цветов):


Видите, как цвет фона изменяется при каждом движении мыши? Это действительно плохо сказывается на производительности, поэтому ведущие специалисты по веб-разработке советовали не использовать выражения CSS или заменить их реальным JavaScript. Однако в те дни немногие хорошо разбирались в написании JavaScript, включая меня. Сегодня я бы сказал, что всё зависит от качества кода: можно нагрузить CPU и плохим кодом JavaScript тоже. Одно из решений проблемы показано в коде псевдоэлемента, где выражение отключается после первого запуска, присваивая статическое this.style или this.runtimeStyle (ещё одно фирменное изобретение Microsoft, представляющее объект style с ещё более высоким приоритетом в каскаде CSS, чем встроенные стили). Если значение должно остаться динамическим, вы могли изменить код — и выполнять дорогостоящие вычисления только когда это необходимо:

<script>
  window.calcWidth = '100%';
  window.updateWidth = false;
  window.onresize = function() {
    window.updateWidth = true;
  };
</script>
<style>
  .element {
    width: expression(
      updateWidth ?
      (
        calcWidth = Math.min(this.parentElement.clientWidth, 400) + 'px',
        updateWidth = false
      ) :
      calcWidth
    );
  }
</style>

Но почему не использовать простой JavaScript? Действительно, всё это можно делать на чистом JavaScript. Но выражения легче запустить для многих различных типов элементов, поскольку вы можете использовать селекторы. А для псевдоэлементов выражение polyfill в CSS имеет ещё больше смысла, потому что здесь же создаются и реальные псевдоэлементы. Так что всё зависит от обстоятельств.

Шрифты


Internet Explorer также был первым браузером, который позволил использовать произвольные шрифты. Соответствующее правило @font-face появилось CSS 2.0, но его удалили в CSS 2.1, поскольку поддержка браузерами оказалась слишком плохой. Microsoft продолжала поддерживать его и связала с новым форматом файлов, который разработали вместе с компанией-разработчиком шрифтов Monotype: это Embedded OpenType (EOT). Формат EOT должен был решить эти проблемы с двух сторон. С одной стороны, средства разработки вроде Microsoft Web Embedding Fonts Tool (WEFT) принимали только исходные шрифты, не отмеченные флагом no embedding. Таким образом, создатели шрифтов могли запретить их использование в интернете. С другой стороны, во время создания вы указываете список разрешённых URL-адресов для шрифта, а браузер будет проверять его и отображать шрифт только в том случае, если текущий URL-адрес совпадает с адресом в списке.

В 2008 году Microsoft и Monotype передали EOT для стандартизации в W3C. Но в конечном счёте разработчики других браузеров его не поддержали, поскольку вместо стандартного алгоритма сжатия gzip он (тогда) использовал запатентованный алгоритм MicroType Express. Поэтому вместо этого они попросили W3C разработать другой формат встраивания шрифтов, только на основе gzip, и так в 2010 году появился формат WOFF.

«Вместо того, чтобы внедрять в шрифт URL документа, он использует функцию HTTP (origin), которая позволяет задать доменную часть URL документов: менее точно, чем полный URL, но достаточно для большинства производителей шрифтов. В конце концов, WOFF всё же принял части алгоритма MicroType Express и новый алгоритм сжатия Brotli, чтобы увеличить степень сжатия по сравнению с gzip».

Интересный факт: когда вы хотите встроить шрифты в презентацию PowerPoint 2007 или 2010, шрифты встраиваются в файл .pptx в формате EOT.

HTML-компоненты: Attached Behaviors, Element Behaviors и Default Behaviors


Ещё в 1998 году Microsoft предлагала методы, близкие к тому, что мы сегодня знаем как CSS Houdini и Web Components. Они назвались HTML Components:

«В последнее время HTML со скриптами всё чаще используется в качестве прикладной платформы для разработки. Одним из сдерживающих факторов является то, что невозможно было формализовать сервисы приложения HTML или повторно использовать их в качестве компонентов на другой HTML-странице или приложении. HTML-компоненты устраняют этот недостаток; HTML-компонент (сокращённо HTC) обеспечивает механизм многоразовой инкапсуляции компонента, реализованного на HTML, CSS и JavaScript.

Компонентизация — мощная парадигма, которая позволяет пользователям создавать приложения, используя «строительные блоки» функциональности, без необходимости реализовывать эти строительные блоки самостоятельно или понимания, как работает вся структура в мельчайших деталях. Этот метод упрощает разработку сложных приложений, разбивая их на более управляемые блоки и позволяя повторно использовать строительные блоки в других приложениях. Компоненты HTML привносят этот мощный метод разработки в веб-приложения».

На самом деле, работу Microsoft признали образцом prior art в начале обсуждения Web Components.

Первым HTML-компоненты реализовал Internet Explorer 5 в 1999 году, а последним — Internet Explorer 9 в 2010 году.

Всего было три типа HTML-компонентов: Attached Behavior, Element Behavior и Default Behavior.

Attached Behavior


Поведение Attached Behavior работало аналогично CSS Houdini Worklet, где у вас есть файл (.htc), который добавляет новые возможности любому элементу, к которому он присоединён. Само присоединение делается через CSS:

img {
  behavior: url(roll.htc);
}

Файлы .htc состояли из специальной XML-разметки для связи с внешним миром и скриптового блока, который определяет, как будет вести себя элемент. Присоединённый элемент DOM был представлен как глобальный element. Следующее Attached Behavior в примере ниже адаптировано для элементов изображения и будет менять их источник каждый раз, когда наводится курсор мыши (спасибо за помощь автору всех последующих примеров Джасперу Пьерру):

<public:attach event="onmouseover" onevent="rollover()" />
<public:attach event="onmouseout" onevent="rollout()" />
<script>
var originalSrc = element.src;
function rollover() {
    element.src = "rollover-" + originalSrc;
}
function rollout() {
    element.src = originalSrc;
}
</script>

Element Behavior


Element Behavior идёт чуть дальше: он переносит в файл .htc не только поведение, но и разметку, тем самым создавая пользовательский элемент. Это очень похоже на элементы Custom Elements в стандарте Web Components. Они тоже снаружи выглядят тривиальными (Light DOM), но внутри скрывают сложную структуру Shadow DOM. Версия Shadow DOM от Microsoft называется Viewlink. Поддержку Viewlink нужно указать явно (opt-in). Как и Shadow DOM, она защитит внутреннюю структуру от любых стилей документа или внешних скриптов, которые пытаются ею манипулировать.

«Viewlink — функция Element Behavior, которая позволяет писать полностью инкапсулированные динамические HTML (DHTML) поведения и импортировать их в качестве надёжных пользовательских элементов на веб-странице».

Для Element Behavior уже было недостаточно привязать элемент HTML к поведению средствами CSS. Вместо этого приходилось использовать возможности XML, создавая для пользовательских компонентов новое пространство имён:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html xmlns:custom>
<head>
  <?import namespace="custom" implementation="RollImgComponent.htc">
</head>

custom — это произвольное название пространства имён XML. Тег <?import> делает то, для чего мы ранее использовали CSS: ссылается на файл .htc с кодом этого компонента. В файл .htc нужно было добавить ещё несколько частей:

  1. Определить пользовательский HTML-тег, под которым будет использоваться элемент: <public:component tagname="rollimg">
  2. Определить любые атрибуты HTML, которые допускает этот элемент, например: <public:property name="src" />
  3. Добавить внутреннюю разметку вашего элемента аналогично современному элементу <template>.

<public:component tagname="rollimg">
  <public:attach event="onmouseover" onevent="rollover()" />
  <public:attach event="onmouseout" onevent="rollout()" />
  <public:property name="src" />
</public:component>

<img id="image" />

<script>
  // IE's document.getElementByID
  var img = document.all['image'];
  img.src = element.src;
  img.id = undefined;
  element.appendChild(img);

  function rollover() {
    img.src = "rollover-" + element.src;
  }
  function rollout() {
    img.src = element.src;
  }
</script>

Обратите внимание, что в пределах файла .htc у вас собственный объект document со своей областью действия. Теперь вы готовы использовать свой пользовательский элемент в разметке HTML:

<custom:rollimg src="logo.png">

Такие пользовательские элементы синхронно анализируются парсером и создаются. В нашем случае DOM страницы после создания элемента будет выглядеть следующим образом:

<custom:rollimg src="logo.png">
  <img src="logo.png" />
</custom:rollimg>

Как видите, эти элементы img стали видимыми, так что с ними можно работать из HTML-документа через обход DOM или CSS. Возможно, это нежелательное свойство. Чтобы исправить это, нужно активировать в Internet Explorer версию Shadow DOM под названием viewLink:

<public:component tagname="rollimg">
  <public:attach event="onmouseover" onevent="rollover()" />
  <public:attach event="onmouseout" onevent="rollout()" />
  <public:property name="src" />
</public:component>

<img id="image" />

<script>
  // Activates IE's Shadow DOM
  defaults.viewLink = document;

  // IE's document.getElementByID
  var img = document.all['image'];
  img.src = element.src;

  function rollover() {
    img.src = "rollover-" + element.src;
  }
  function rollout() {
    img.src = element.src;
  }
</script>

Default Behavior


Поведение по умолчанию — третий вариант HTML-компонентов. Это в основном стандартные библиотеки, встроенные в браузер, которые вы можете использовать через расширение CSS или XML и которые открывают совершенно новый набор функций.

Запуск загрузки


Одна из них — behavior:url(#default#download).

В настоящее время, когда вы хотите запустить загрузку просто с фронтенда, вы можете поставить ссылку с атрибутом download и выполнить на ней .click(). Ну, а в прежние времена было так:

<!--
the following element needs to be created once in the document.
It then exposes new utility methods you can use, like .startDownload()
-->
<span id="download" style="behavior:url(#default#download)"></span>

<button onclick="download.startDownload('menu.pdf')">Download</button>

Хранение данных


Другим очень полезным поведением по умолчанию было behavior: url(#default#userData). Оно решало те же проблемы, что и localStorage, только совершенно иначе. Ниже показано, как сохранить и восстановить значения элементов input в старом IE:

<style>
  #store {
    behavior: url(#default#userData);
  }
</style>

<script>
  function save(){
    store.setAttribute('username', username.value);
    store.setAttribute('email', email.value);
    store.save('exampleStore');
  }
  function restore(){
    store.load('exampleStore');
    username.value = store.getAttribute('username');
    email.value = store.getAttribute('email');
  }
</script>

<span id="store"></span>
<input id="username">
<input id="email">
<button onclick="restore()">restore values</button>
<button onclick="save()">save values</button>

Были даже полифиллы localStorage для IE, которые использовали данную технику.

Client Capabilities


Другим поведением по умолчанию является Client Capabilities. Как намекает название, оно позволяет больше узнать о клиенте. Наиболее интересной информацией является тип подключения, аналогично современному navigator.offline или Network Information API:

<span id="clientcapabilities"
      style="behavior:url(#default#clientCaps)">
</span>

<script>
  // Either "modem" or "lan" or "offline"
  var connectionType = clientcapabilities.connectionType;
</script>

Анимация через TIME


Думаете, Internet Explorer не поддерживает анимацию? На самом деле в те времена уже существовал SMIL, Synchronized Multimedia Integration Language. Это язык разметки для описания мультимедийных презентаций, определяющий разметку для синхронизации, компоновки, анимации, визуальных переходов и встраивания мультимедиа. Хотя Microsoft активно участвовала в создании этого нового стандарта W3C, она в конечном счёте решила не внедрять его в Internet Explorer. Вместо этого Microsoft вывела из него диалект, который интегрировала с HTML и тоже представила в W3C для стандартизации: это HTML+TIME (Timed Interactive Multimedia Extensions). Позже W3C переработала его в нечто, ставшее частью SMIL 2.0 и получившее название XHTML+TIME. В 1999 году Internet Explorer 5 стал первым браузером с поддержкой этой технологии в версии 1.0. Год спустя в Internet Explorer 5.5 внедрили HTML+TIME версии 2.0, реализация которой была ближе к черновику XHTML+TIME от W3C. Впрочем, Microsoft продолжала придерживаться старого названия, без первой буквы 'X'.

Эта функция также инкапсулировалась в Default Behavior, которое следовало сначала активировать либо с помощью CSS, либо путём расширения пространства имён (XML). После этого становились доступны различные функции анимации. Например, отображение и скрытие элементов пять раз подряд по десять секунд:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
  <style>
    .time {
      behavior: url(#default#time2);
    }
  </style>
</head>
<body>
<div class="time" repeatcount="5" dur="10" timecontainer="par">
  <p class="time" begin="0" dur="4">First line of text.</p>
  <p class="time" begin="2" dur="4">Second line of text.</p>
  <p class="time" begin="4" dur="4">Third line of text.</p>
  <p class="time" begin="6" dur="4">Fourth line of text.</p>
</div>
</body>
</html>


Или, переключившись на вариант пространства имён XML, вы можете анимировать HTML-атрибуты, такие как цвет фона body:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html xmlns:t="urn:schemas-microsoft-com:time">
<head>
  <?import namespace="t" implementation="#default#time2">
</head>
<body id="body">
  <t:animateColor targetElement="body"
    attributeName="backgroundColor"
    from="black" to="white"
    begin="0" dur="3" fill="hold"/>
</body>
</html>


Или можете встроить видео или аудио в HTML, аналогично тому, как вы сегодня используете <video> и <audio>, даже с элементами управления:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html xmlns:t ="urn:schemas-microsoft-com:time">
<head>
  <?import namespace="t" implementation="#default#time2">
</head>
<body>
  <t:video id="video" src="video.mpeg" type="video/mpeg"/>
  <div class="controls">
    <button type="button" onclick="video.resumeElement()">play</button>
    <button type="button" onclick="video.pauseElement()">pause</button>
    <button type="button" onclick="video.speed = 1">1x</button>
    <button type="button" onclick="video.speed = 4">4x</button>
  </div>
</body>
</html>


Поддерживались все форматы, для которых базовая операционная система Windows находила соответствующий декодер. По умолчанию это были, например, MPEG-1 и AVI, закодированные с помощью кодека Microsoft Video-1.

Особый формат от Microsoft вы даже могли сочетать с HTML5:

<video id="html5video" autoplay muted>
  <source src="video.mp4" type="video/mp4"/>
  <t:video id="video" src="video.mpeg" type="video/mpeg"/>
</video>
<div class="controls">
  <button type="button"
    onclick="html5video.play ? html5video.play() : video.resumeElement()">play</button>
  <button type="button"
    onclick="html5video.pause ? html5video.pause() : video.pauseElement()">pause</button>
  <button type="button"
    onclick="html5video.playbackRate = video.speed = 1">1x</button>
  <button type="button"
    onclick="html5video.playbackRate = video.speed = 4">4x</button>
</div>

См. демо. Чтобы запустить оригинальный код, понадобится виртуальная машина с IE 5.5-8.

Vector Markup Language


В 1999 году Microsoft также стала первой, кто поддержал формат векторной графики в браузере: язык векторной разметки Vector Markup Language, сокращённо VML. Он был разработан Autodesk, Hewlett-Packard, Macromedia, Microsoft и Vision и представлен W3C в 1998 году. К сожалению, примерно в то же время W3C получил конкурирующую заявку под названием Precision Graphics Markup Language (PGML), разработанную Adobe Systems и Sun Microsystems. Таким образом, W3C объединила оба предложения и представила формат масштабируемой векторной графики Scalable Vector Graphics (SVG) в 2001 году. Первым браузером с поддержкой SVG стал Konqueror 3.2 в 2004 году.

Честно говоря, к VML не было особых претензий, кроме того, что он работал исключительно внутри HTML, а не по внешней ссылке. Вот как создаётся эллипс в SVG и VML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SVG Ellipse</title>
</head>
<body>
<svg>
  <ellipse cx="200"
    cy="80"
    rx="100"
    ry="50"
    fill="yellow"
    stroke="purple"
    stroke-width="2" />
</svg>
</body>
</html>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html xmlns:v="urn:schemas-microsoft-com:vml" lang="en">
<head>
  <title>VML Ellipse</title>
  <style>v\:* {behavior:url(#default#VML);}</style>
</head>
<body>
<v:oval
  style="position: absolute; width: 200; height: 100; left: 100; top: 30;"
  fillcolor="yellow"
  strokecolor="purple"
  strokeweight="2">
</v:oval>
</body>
</html>

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

Привязка данных


Начиная с Internet Explorer 4.0 в 1997 году, вы могли встраивать в свой документ источники данных. Это делается путём ссылки на внешний файл CSV через элемент <object>:

<object id="data" classid="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
 <param name="DataURL" value="data.csv">
</object>

Вместо CSV-файла можно установить соединение с сервером баз данных (не рекомендуется в режиме записи :)

<object id="data" classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33">
    <param name="Server"  value="http://server">
    <param name="Connect" value="dsn=database;uid=guest;pwd=">
    <param name="SQL"     value="select name from people">
</object>

(Обратите внимание, как изменяется атрибут classid в зависимости от типа данных).

Наконец, можно сослаться на внешний XML-файл с помощью тега <xml>

<xml src="http://localhost/xmlFile.xml"></xml>

…или XML, встроенный внутрь HTML как источник данных:

<body>
<xml id="data">
  <?xml version="1.0" encoding="UTF-8" ?>
  <records>
    <record>
      <name>Willa Galloway</name>
      <email>tortor@dictum.com</email>
      <phone>098-122-8540</phone>
      <city>Tenali</city>
    </record>
    <record>
      ...
    </record>
    ...
  </records>
</xml>
</body>

Такой встроенный фрагмент XML называется XML Data Island и обрабатывается примерно так же, как сегодня обрабатываются SVG, встроенные в HTML, или HTML, встроенные в SVG <foreignObject>, переключая парсер на лету.

«XML Data Islands — это XML-документ внутри HTML-страницы. Такой подход позволяет избежать необходимости писать код (т. е. скрипт) лишь для загрузки XML-документа или его загрузки через тег. Скрипты с клиентской стороны могут напрямую обращаться к этим «островам данных XML», и они также могут быть привязаны к HTML-элементам».

Теперь, когда у вас на странице есть данные, можете использовать привязку данных Internet Explorer, например, для редактирования:

<input type="text" datasrc="#data" datafld="name">

Двусторонняя привязка данных


Вы можете привязывать данные не только к input, но и к произвольным элементам. И можете создать двусторонний поток данных, просто с декларативной разметкой:

<xml id="data">
  <record>
    <name></name>
  </record>
</xml>
<h1>Hello, <span datasrc="#data" datafld="name"></span>!</h1>
<label>Your name: <input datasrc="#data"
                         datafld="name"
                         onkeyup="this.blur();this.focus()">
</label>

Единственная функция onkeyup="this.blur();this.focus()" — инициировать поток данных после каждого нажатия клавиши, так как в противном случае другие подключённые элементы получат обновленное значение только после действия пользователя (input).


Data Grid


Internet Explorer поставлялся с нативной реализацией data grid, подключенной к указанным выше источникам данных и построенной поверх элемента <table>. Это называлось Tabular Data Control. Для сопоставления полей данных со столбцами таблицы использовались атрибуты datafld, для включения или отключения HTML-кодирования содержащихся данных — атрибут dataformatas, а для обозначения количества записей, которые должна одновременно отображать страница таблицы — атрибут datapagesize. И были ещё методы previousPage, nextPage, firstPage и lastPage для простой навигации по страницам.

<table id="datagrid"
       datasrc="#people"
       datapagesize="10">
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Phone</th>
      <th>City</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><span datafld="name"></span></td>
      <td><span datafld="email"></span></td>
      <td><span datafld="phone"></span></td>
      <td><span datafld="city"></span></td>
    </tr>
  </tbody>
</table>
<button onclick="datagrid.previousPage()">< previous</button>
<button onclick="datagrid.nextPage()">next ></button>


Событие PropertyChange


Internet Explorer поддерживал интересное событие propertychange, которое можно было присоединить к элементам DOM. Оно запускалось каждый раз, когда программно изменялось одно из его свойств, что могло произойти через setAttribute() или через доступ к свойству объекта. Это позволяло создать наблюдаемые/инструментированные объекты для таких ситуаций, в которых сегодня используется ES6 Proxy. Нужен был только фиктивный элемент DOM с событием propertychange:

<div id="store"></div>

<script>
  function handler() {
    // A magic 'event' variable is passed into
    // the event handler function. When coming
    // from the 'propertychange' event, it comes
    // with a 'propertyName' property.

    if (event.propertyName === 'onpropertychange') {
      // Don't execute right at the beginning on itself
      return;
    }

    alert(
        event.propertyName +
        '\'s value was changed/set to "' +
        store[event.propertyName] +
        '"'
    );
  }

  store.onpropertychange = handler;

  store.test = true;
  store.test = false;
  store.literal = {};
  store.literal = {
    key: 'value2'
  };
</script>

Событие изменения размера элементов DOM


В Internet Explorer было много уникальных событий, но самым интересным из них является событие изменения размера элементов, доступное вплоть до IE 9.

«Событие onresize срабатывает для блочных и встроенных объектов с макетом, даже если изменяются значения свойств документа или CSS».

Это событие похоже на современные Resize Observer и Mutation Observer. Отличие от обычных событий в том, что оно срабатывает асинхронно, как observer.

element.onresize = function(e) {
    /* React to the element's size change */
}

Предварительная загрузка JavaScript


Одной из интересных особенностей Internet Explorer была загрузка и выполнение кода из источников, добавленных через скрипт:

var script = document.createElement('script');
var head = document.getElementsByTagName('head')[0];

script.src = '...';
head.appendChild(script);

Другие браузеры извлекали и выполняли такой скрипт в момент добавления к DOM. У Internet Explorer был более тонкий подход: он разделял оба шага. Загрузка происходила в момент присвоения свойства .src, в то время как выполнение начиналось только после добавления скрипта к DOM. Таким образом, можно предзагружать скрипты, не блокируя основной поток. Для других браузеров подобную функциональность можно было реализовать только более сложным способом, по крайней мере, пока у нас не появились Resource Hints.

Internet Explorer также был первым браузером, который ввёл для скриптов атрибут defer.

Нахождение текущего выполняемого скрипта


В какой-то момент в стандарт HTML5 добавили свойство document.currentScript, которое указывает на выполняемый в данный момент элемент <script>. Зачем это нужно? Например, чтобы прочитать дополнительную конфигурацию, как эта в атрибуте data-main:

<script src="scripts/require.js" data-main="js/main"></script>

Где-то внутри scripts/require.js будет такая строка:

var main = document.currentScript.getAttribute('data-main');

К сожалению, это реализовали только в Edge 12. Мало кто знал, что в Internet Explorer встроен другой механизм, который не только даёт тот же результат, но и лучше работает, если документ всё ещё загружается или полностью интерактивен: свойство скрипта .readyState.

function currentScript() {
  var scripts = document.getElementsByTagName('script');

  for (; i < scripts.length; i++) {
    // If ready state is interactive, return the script tag
    if (scripts[i].readyState === 'interactive') {
      return scripts[i];
    }
  }

  return null;
}

Свойство .readyState удалили в Internet Explorer 11, так что это единственная версия, которая не поддерживает ни .currentScript, ни .readyState (к счастью, гений по имени Адам Миллер нашёл другой способ для полифиллинга).

Так что привело к закату IE?


Глядя на приведённый выше список, я бы сказал, что Microsoft на несколько световых лет опередила всех в разработке инструментов для проектирования сложных и восхитительных веб-сайтов. Некоторые синтаксические конструкции могут показаться нам незнакомыми, но это только потому, что мы к ним не привыкли. В те дни был в моде XML. А помните, что вы почувствовали, когда впервые открыли SVG? Или когда увидели стрелочные функции ES6? Или BEM? JSX? Наверное, у вас были такие же чувства.

Одна из причин, почему не прижились идеи Microsoft, заключалась в том, что мы, разработчики, просто не поняли их. Большинство из нас были любителями и не имели компьютерного образования. Вместо этого мы были так заняты изучением семантической разметки и CSS, что полностью пропустили остальное. На мой взгляд, слишком мало людей в то время достаточно свободно владели JavaScript, не говоря уже об архитектуре сложных приложений на JavaScript, чтобы оценить такие вещи, как компоненты HTML, привязки данных или Default Behavior. Не говоря уже об этих странных островах данных и VML.

Другой причиной могло быть отсутствие платформ для распространения знаний в массы. Интернет всё ещё находился в зачаточном состоянии, не было ни MDN, ни Smashing Magazine, ни Codepen, ни Hackernoon, ни Dev.to, и почти никаких личных блогов со статьями на эти темы. Кроме Webmonkey. Кроме того, ещё не проводились конференции, где разработчики из Microsoft могли бы рассказать об этих инновациях. Не было даже широкополосных интернет-соединений, то есть невозможно было организовать даже видеоконференцию. Всё, что было, это списки рассылки типа A List Apart и IRC-каналы для связи: аналог нынешнего Slack, только без всей мишуры.

Последним гвоздём в крышку гроба стало решение Microsoft после выхода Internet Explorer 6 привязать новые версии Internet Explorer к новым версиям Windows. Поэтому они распустили команду Internet Explorer и интегрировали её в продуктовую группу Windows. К сожалению, следующая версия Windows, преемница Windows XP с кодовым именем Longhorn (позже Windows Vista), серьёзно задержалась. Разработка велась настолько хаотично, что им пришлось даже перезапускать процесс. Это также задержало выпуск нового Internet Explorer и оставило браузер в подвешенном состоянии, без исправления ошибок и улучшения существующих технологий. Когда Microsoft проснулась пять лет спустя, было уже слишком поздно. W3C разработала новые стандарты, а остальные разработчики браузеров не только внедрили их, но и основали рабочую группу WHATWG, которая принесла ещё больше инноваций. Microsoft потеряла техническое лидерство, потеряла свою долю рынка и никогда не оправилась от этого потрясения.


На КДПВ: развалины монумента Болгарской коммунистической партии на пике Бузлуджа в Центральной Болгарии. Фото Натальи Летуновой на Unsplash