Интересные трюки HTML, CSS и JS
Здесь вы найдёте небольшую подборку нестандартных вариантов использования HTML/CSS/JS. Если информация окажется полезной, будем собирать эти хаки на постоянной основе и публиковать по мере накопления.
Примечание. Некоторые трюки основаны на открытых уязвимостях браузеров и поисковой системы Google и др. Поэтому могут перестать работать в ближайшее время (или продолжат, если разработчики не признают баги и не захотят их исправлять). Другие функции работают только начиная с конкретных версий Chrome, Firefox и т. д.
▍ Отслеживание поведения пользователей на сайтах конкурентов
Маркетолог и SEO-оптимизатор Дэн Петрович нашёл способ, как отслеживать действия пользователей на сайтах конкурентов, включая полную информацию о просмотрах страниц, переходах по ссылкам, времени просмотра, видеозаписях сессий и нажатиях клавиш. Можно собирать всю информацию, которая вводится в веб-формы на чужом сайте, и получать её к себе по электронной почте. Всё это делается с помощью подделки чужого сайта и заманивания юзеров на это зеркало. Способ не совсем этичный, поэтому публикуем информацию исключительно в образовательных целях.
Алгоритм:
- Пользователь заходит на нашу страницу из поиска (referrer: google).
- Наша веб-страница перехватывает нажатие кнопки «Назад» в Chrome и подсовывает пользователю нашу копию поисковой выдачи Google.
- Переход по любой ссылке на сайт конкурента ведёт на наше зеркало сайта конкурента.
- На своём зеркале мы можем записывать все действия пользователя, генерировать хетмапы, скроллмапы, нажатия клавиш, а также перенаправить его на оригинальный сайт конкурента или на другие сайты в случае необходимости (например, для совершения покупки). В идеале пользователь ничего не заметит.
Примечание 1. Альтернативный способ заведения пользователей на зеркало чужого сайта — через поисковую рекламу. На самом деле в интернете полно фейковых копий (зеркал) аутентичных сайтов, в том числе Хабра, куда заходят тысячи пользователей из поиска, не подозревая о подделке.
Примечание 2. Несколько лет назад похожий способ использовался для перехвата звонков через публикацию фейковых объявлений на Google Maps со своим телефонным номером вместо оригинальных номеров ФБР и Секретной службы США.
В результате пользователи звонят по нашим номерам и сообщают информацию, которую хотели передать спецслужбам.
▍ Редактируемые страницы HTML
Нажатие кнопки DesignMode ON/OFF
делает веб-страницу редактируемой. Нужна всего одна строчка JavaScript в коде:
document.designMode = "on";
Это древние API появились 25 лет назад, но до сих пор поддерживаются в браузерах.
▍ Грамотное использование
В последние годы во многих браузерах появилась поддержка современных форматов сжатия изображений AVIF и WebP (к сожалению, 10 декабря 2022 года Google удалила поддержку JPEG XL из Chromium, так что этот формат теперь не слишком актуален). Можно сократить трафик со своего сайта, если включить эту оптимизацию с помощью тега .
Идея в том, чтобы выдавать AVIF и WebP только тем браузерам, которые это поддерживают.
- Конвертируем изображения в другой формат. Например, с помощью кроссплатформенной утилиты imagemagick или сервиса avif.io.
$ magick convert image.jpg image.avif
Аналогичную конвертацию производим для каждой пары форматов (jpg/avif, jpg/webp). - Вместо традиционного
, прописываем тег
, который допускает перечисление нескольких URL для одной картинки. Первым указываем AVIF как самый маленький формат. Нормальные браузеры загрузят его, а Edge и все остальные, которые не поддерживают AVIF, пойдут дальше по списку: WebP и JPEG.
Все варианты указываем в теге, и только последний в теге
.
- Известная оптимизация для более быстрой загрузки страниц — настроить HTTP/2 Server Push на сервере, чтобы ресурсы загружались сразу, не дожидаясь их запроса после парсинга HTML. В нашем случае для элементов
не оптимально грузить клиенту всё подряд. Лучше распознать браузерный юзер-агент и заранее пушить картинку соответствующего формата. Специально для этого предназначено поле
Accept
в user-agent. Например, последний Firefox высылаетAccept
такого вида:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8
▍ Хранение состояния приложения в URL
Если у вас есть какое-то веб-приложение и вы хотите дать возможность людям работать в нём без авторизации на сайте и хранения данных на сервере, то для этого есть интересный хак: сохранять состояние в URL, в браузере клиента.
Это даёт возможность пользователю «сохраниться», а позже продолжить работу (редактирование данных). Также удобно делиться результатами работы с коллегами просто путём отправки URL.
Состояние приложения — файл JSON, он кодируется в строку, потом сжимается и переводится в байты Base64, которые добавляются в URL после символов `/g/a#`. Например, таким образом:
наш_сайт.com/g/a#N4IgzgpgTglghgGxgLwnARgiAxA9lAWxAC5QA7X...
Псевдокод для реализации такой функциональности:
const stateString = JSON.stringify(appState); // appState представляет собой объект JSON
const compressed = compress(stateString);
const encoded = Base64.encode(compressed);
// Пушим закодированную строку в url
// ...Позже при загрузке страницы или undo/redo читаем url и
// делаем следующее
const decoded = Base64.decode(encoded); // в той же кодировке, что и выше, но считывается из url
const uncompressed = uncompress(decoded);
const newState = JSON.parse(uncompressed);
// Загружаем приложение с новым состоянием newState
URL обновляется после каждого изменения состояния приложения, так что скопировать текущее состояние можно простым копипастом из адресной строки браузера. Примеры, как это работает: интерактивные редакторы блок-схем knotend и mermaid.live:
Есть несколько вариантов, как реализовать функции сжатия/декомпрессии, например, lz-stirng или pako.
В качестве бонуса мы получаем для своего веб-приложения «бесплатную» функциональность undo/redo из браузерного стека, а также автоматическое внедрение на любую стороннюю веб-страницу, которая поддерживает embed’ы.
Можно ещё посмотреть, как Yahoo! Finance хранит настройки пользователя в URL:
https://finance.yahoo.com/quote/F/chart?p=F#eyJpbnRlcnZhbCI6IndlZWsiLCJwZXJpb2RpY2l0eSI6MSwidGltZVVuaXQiOm51bGwsImNhbmRsZVdpZHRoIjo0LjM0ODY1OTAwMzgzMTQxNzUsImZsaXBwZWQiOmZhbHNlLCJ2b2x1bWVVbmRlcmxheSI6dHJ1ZSwiYWRqIjp0cnVlLCJjcm9zc2hhaXIiOnRydWUsImNoYXJ0VHlwZSI6ImxpbmUiLCJleHRlbmRlZCI6ZmFsc2UsIm1hcmtldFNlc3Npb25zIjp7fSwiYWdncmVnYXRpb25UeXBlIjoib2hsYyIsImNoYXJ0U2NhbGUiOiJsaW5lYXIiLCJzdHVkaWVzIjp7IuKAjHZvbCB1bmRy4oCMIjp7InR5cGUiOiJ2b2wgdW5kciIsImlucHV0cyI6eyJpZCI6IuKAjHZvbCB1bmRy4oCMIiwiZGlzcGxheSI6IuKAjHZvbCB1bmRy4oCMIn0sIm91dHB1dHMiOnsiVXAgVm9sdW1lIjoiIzAwYjA2MSIsIkRvd24gVm9sdW1lIjoiI2ZmMzMzYSJ9LCJwYW5lbCI6ImNoYXJ0IiwicGFyYW1ldGVycyI6eyJ3aWR0aEZhY3RvciI6MC40NSwiY2hhcnROYW1lIjoiY2hhcnQifX19LCJwYW5lbHMiOnsiY2hhcnQiOnsicGVyY2VudCI6MSwiZGlzcGxheSI6IkYiLCJjaGFydE5hbWUiOiJjaGFydCIsImluZGV4IjowLCJ5QXhpcyI6eyJuYW1lIjoiY2hhcnQiLCJwb3NpdGlvbiI6bnVsbH0sInlheGlzTEhTIjpbXSwieWF4aXNSSFMiOlsiY2hhcnQiLCLigIx2b2wgdW5kcuKAjCJdfX0sInNldFNwYW4iOnsibXVsdGlwbGllciI6NSwiYmFzZSI6InllYXIiLCJwZXJpb2RpY2l0eSI6eyJwZXJpb2QiOjEsImludGVydmFsIjoid2VlayJ9fSwibGluZVdpZHRoIjoyLCJzdHJpcGVkQmFja2dyb3VuZCI6dHJ1ZSwiZXZlbnRzIjp0cnVlLCJjb2xvciI6IiMwMDgxZjIiLCJzdHJpcGVkQmFja2dyb3VkIjp0cnVlLCJldmVudE1hcCI6eyJjb3Jwb3JhdGUiOnsiZGl2cyI6dHJ1ZSwic3BsaXRzIjp0cnVlfSwic2lnRGV2Ijp7fX0sImN1c3RvbVJhbmdlIjpudWxsLCJzeW1ib2xzIjpbeyJzeW1ib2wiOiJGIiwic3ltYm9sT2JqZWN0Ijp7InN5bWJvbCI6IkYiLCJxdW90ZVR5cGUiOiJFUVVJVFkiLCJleGNoYW5nZVRpbWVab25lIjoiQW1lcmljYS9OZXdfWW9yayJ9LCJwZXJpb2RpY2l0eSI6MSwiaW50ZXJ2YWwiOiJ3ZWVrIiwidGltZVVuaXQiOm51bGwsInNldFNwYW4iOnsibXVsdGlwbGllciI6NSwiYmFzZSI6InllYXIiLCJwZXJpb2RpY2l0eSI6eyJwZXJpb2QiOjEsImludGVydmFsIjoid2VlayJ9fX1dfQ--
{"interval":"week","periodicity":1,"timeUnit":null,"candleWidth":4.3486590038314175,"flipped":false,"volumeUnderlay":true,"adj":true,"crosshair":true,"chartType":"line","extended":false,"marketSessions":{},"aggregationType":"ohlc","chartScale":"linear","studies":{" vol undr ":{"type":"vol undr","inputs":{"id":" vol undr ","display":" vol undr "},"outputs":{"Up Volume":"#00b061","Down Volume":"#ff333a"},"panel":"chart","parameters":{"widthFactor":0.45,"chartName":"chart"}}},"panels":{"chart":{"percent":1,"display":"F","chartName":"chart","index":0,"yAxis":{"name":"chart","position":null},"yaxisLHS":[],"yaxisRHS":["chart"," vol undr "]}},"setSpan":{"multiplier":5,"base":"year","periodicity":{"period":1,"interval":"week"}},"lineWidth":2,"stripedBackground":true,"events":true,"color":"#0081f2","stripedBackgroud":true,"eventMap":{"corporate":{"divs":true,"splits":true},"sigDev":{}},"customRange":null,"symbols":[{"symbol":"F","symbolObject":{"symbol":"F","quoteType":"EQUITY","exchangeTimeZone":"America/New_York"},"periodicity":1,"interval":"week","timeUnit":null,"setSpan":{"multiplier":5,"base":"year","periodicity":{"period":1,"interval":"week"}}}]}
Наверное, тут много лишней информации, но сам подход понятен.
Примечание. Поскольку браузеры могут обрезать слишком длинные URL, для данных целей надёжнее использовать стандарт URI-фрагментов (строка после знака #
). Как говорится, всё новое — это хорошо забытое старое. Криптохостинг Mega.co.nz реализовал этот метод для передачи ключа расшифровки ровно десять лет назад.
▍ Браузерные API сообщают владельцу сайта о падениях на его странице
Пару лет назад Chrome реализовал интересный набор интерфейсов Reporting API, о котором до сих пор знают не все разработчики.
Через эти API браузер следит за пользователями вашего сайта и присылает отчёты по падениям/ошибкам с их стороны, просто отправляя JSON по указанному URL.
Правда, в данный момент в Chrome две несовместимые версии API, так что нужно разбираться, какую из версий лучше использовать.
Благодаря этим API можно найти нестандартные ошибки на своих страницах. Например, частые падения мобильных браузеров из-за CSS-анимаций. Оказывается, стандартные CSS-анимации непосильны для старых смартфонов — браузер вылетает из-за недостатка RAM.
▍ Тригонометрические функции CSS
Если речь зашла о CSS, то пару месяцев назад все ведущие браузеры добавили поддержку тригонометрических функций.
Многие знают, что в свойствах объектов CSS можно использовать математические выражения: от вычислений calc()
до логических функций min()
, max()
и clamp()
.
А начиная с версии Chrome 111, Firefox 108 и Safari 15.4 появилась ещё поддержка тригонометрических функций sin()
, cos()
, tan()
, asin()
, acos()
, atan()
и atan2()
. Все они описаны в спецификациях CSS Values and Units Module Level 4.
Например, вот код для вращения точки по кругу вокруг центральной точки:
:root {
--radius: 20vmin;
}
.dot {
--angle: 30deg;
translate: /* Translation on X-axis */
calc(cos(var(--angle)) * var(--radius))
/* Translation on Y-axis */
calc(sin(var(--angle)) * var(--radius) * -1)
;
}
Демо:
См. также анимированное кольцо Мёбиуса на синусах и косинусах.
P. S. О нестандартных способах применения HTML/CSS/JS, которые вас удивили, пишите в комментариях или в личку.
Пол-лимона подарков от RUVDS. Отвечай на вопросы и получай призы