Ленивая загрузка изображений с доступностью и поддержкой принтера

Я ищу правильный способ реализовать ленивую загрузку изображений без ущерба для печати и доступности, а также без изменения макета (перехода содержимого), предпочтительно с использованием родного loading=lazy и запасного варианта для старых браузеров. Ответы на вопрос Как работает ленивая загрузка изображений с помощью JavaScript? включены различные решения, ни одно из которых полностью не удовлетворяет всем этим требованиям.

Элегантное решение должно быть основано на действительной и полной html-разметке, т. е. с использованием атрибутов <img src, srcset, sizes, width, height и loading вместо помещения данных в атрибуты data-, как в популярных библиотеках javascript lazysizes и vanilla -lazyload делать. Также не должно быть необходимости использовать элементы <noscript>.

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

Обе библиотеки javascript, упомянутые выше, требуют либо недопустимой разметки без какого-либо атрибута src вообще, либо пустого или низкокачественного заполнителя (LQIP), в то время как данные src вместо этого помещаются в data-src, а данные srcset помещаются в data-srcset, и все это работает только с джаваскриптом. Считается ли это приемлемой или даже лучшей практикой в ​​2020 году, и не вредит ли это доступности сайта, совместимости между устройствами или поисковой оптимизации?

Обновлять:

Я попробовал обходной путь для ошибки печати, используя только HTML и CSS @media print фоновые изображения в этой кодовой ручке. Даже если бы это работало как задумано, для каждого изображения была бы необходимая директива css, которая не является ни элегантной, ни универсальной. К сожалению, внутри элемента <picture> также нельзя использовать медиа-запросы.

Есть еще один обходной путь, предложенный Хуссейном Джирдехом по адресу lazy-load-with-print-ctl1l4wu1.now.sh с использованием javascript для изменения loading=lazy на loading=eager при нажатии кнопки «печать». Эту же функцию можно использовать на перед печатью.

Я сделал codepen, используя lazysizes.

Я сделал еще один codepen с помощью vanilla-lazyload.

Я думал о разветвлении решения javascript, чтобы заставить его работать с использованием src и srcset, но это, вероятно, должно было быть опробовано раньше, компромисс будет заключаться в том, что как только скрипт отложенной загрузки начнет действовать на элементы изображения, браузер, возможно, уже начал загрузку исходные файлы.

Невозможно выбрать каждый элемент в вашем списке белья и избежать тегов <noscript>, вам нужен запасной вариант на случай сбоя JS. Вы хотите показать нам, как вы пытались решить эти проблемы и где вы боретесь, чтобы мы могли помочь, поскольку на данный момент это слишком близко к вопросу «как я?» / основанному на мнении, я думаю, что вопрос имеет много заслуг, просто нужно немного больше внимания.

GrahamTheDevRel 20.12.2020 11:06

@graham-ritchie Я добавил ссылки на примеры частичных решений.

Ingo Steinke 20.12.2020 13:54

Итак, я полагаю, что последний вопрос заключается в том, где вы готовы пойти на компромисс. Например, пример с Хусейном Джирде работает, только если вы нажмете его кнопку печати (после чего он просто ждет, пока все изображения загрузятся, пока не позвонит print(), поэтому при медленном соединении страница может зависнуть на несколько секунд и более). Печать важнее для вас, чем сохранение пропускной способности? Мы предполагаем, что люди не будут печатать на мобильных устройствах? Я не думаю, что мы можем создать идеальное решение, но если мы сделаем определенные предположения, я думаю, мы сможем приблизиться к этому.

GrahamTheDevRel 20.12.2020 16:11

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

GrahamTheDevRel 20.12.2020 16:24

Спасибо @graham-ritchie, я посмотрю

Ingo Steinke 22.12.2020 09:08

См. также этот вопрос whatwg о проблеме с использованием встроенной ленивой загрузки и печати: github.com/whatwg/html/issues/6581

Tom 15.04.2021 10:21

Подведение итогов на конец 2021 года о состоянии ленивой загрузки для печати: dev.to/ingosteinke/printable-lazy-loading-3hb6

Ingo Steinke 11.01.2022 09:27

Обновление от 20 января 2022 г.: изображение для печати с отложенной загрузкой должно было быть исправлено в последней версии Chrome canary, см. bugs.chromium.org/p/chromium/issues/detail?id=875403#c36

Ingo Steinke 20.01.2022 14:16
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
10
8
2 222
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

  • Сведите к минимуму ненужную загрузку файлов с сервера — это критично для пропускной способности, если у вас большая пользовательская база. Так что это была интернет-версия как раз вовремя, как и в промышленном производстве.
  • Устаревшие версии браузера и до того, как асинхронность и отсрочка были популяризированы в JS/HTML, интерактивность с окном браузера оставалась затрудненной до тех пор, пока весь контент не был загружен.
  • Теперь широкая полоса, как мы ее знаем, существует только последние 6-7 лет в реальном смысле манеры и проникновения. Мы хотели этого, потому что не хотели столкнуться с номером 2 при низкой пропускной способности. Честно говоря, была и до сих пор растет озабоченность и идеология минимизации и сжатия файлов JS и CSS — все потому, что этот круговой путь к серверу и обратно должен быть минимизирован, чтобы можно было получить следующий элемент в списке. Имейте в виду, что браузеры, как правило, ограничивают одновременную загрузку соединений примерно 6 за раз на окно или активное окно. Есть причины, по которым Google популяризировал правило трех секунд. Если наверху дать ему побегать, то правило 3-х секунд упадет ему на голову, как если бы у него не было ног.
  • Так появились мыслительные рамки.
  • Изображение в качестве фона CSS: это произошло потому, что оно не испортило визуальный аспект страницы. Все осталось как есть на своих местах, а потом вдруг стало красочным. Это было время, когда веб-страницы, казалось, имели упругую посадку, т.е. это был тот самый мешок, который когда-то наполнялся воздухом, вдруг лопнул-превратился в прыгающий замок. Это становилось все более плохой идеей для фронтенд-разработчика. Таким образом, исправление высоты и с контейнером, а затем запуск изображений в качестве фона помогло, и свойства выравнивания фона HTML5 соответственно обновили их. Был даже вариант, который до сих пор используется, поскольку используется несколько фонов, один из которых загружает спиральную или низкоуровневую версию размытого изображения, поверх которой было выбрано фактическое предполагаемое изображение. Поскольку фон нижнего уровня будет загружаться и заполняться везде в одном экземпляре загрузки, это создает более приятный визуальный эффект, и пользователь знал, чего ожидать. работало и при печати, даже если предполагаемое изображение не загружалось.
  • Затем появилась JS-версия с захватом DOM либо через data-src, либо с удалением неверных тегов изображений src, либо с чем-то еще. инициировать изменение только при прокрутке содержимого. Очевидно, что будет отставание, но это было либо противопоставлено подходу CSS, реализованному в JS, либо вычислению точек прокрутки и запуску события на пару пикселей вперед. Все они по-прежнему работают на одной и той же предпосылке.

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

Это главная проблема здесь. BOM не заботится и даже не хочет заботиться о том, что ваш скрипт просит сделать все, что он знает, если есть свойство src для извлечения содержимого. Ни одно из решений не изменило это. Если бы мы могли изменить эту функциональность, то мыслительная структура стала бы решением.

Я по-прежнему считаю, что браузеры не должны менять это просто так, и поэтому никогда не получали отслеживания в дебатах. Что сделали браузеры, так это предварительную выборку, известную как спекулятивный или упреждающий предварительный анализ. Это единственное самое большое улучшение в браузерах, которое заслуживает похвалы. Точно так же, как мы вводим URL-адрес в адресной строке при каждом изменении строки, браузер предварительно извлекает содержимое, даже если я не набрал весь URL-адрес. Я специально разработал программу, в которой я перехватывал все, что было получено на сервере от этих предварительных парсеров. В большинстве случаев для получения ответа требуется меньше секунды, и браузеры начинают обрабатывать все это, включая изображения и JS. Это противоречило рывкам с задержкой в ​​эластичном положении лежа, как обсуждалось в № 1 и № 2. Однако это не уменьшило попадание на сервер. Причина, по которой мы делаем ленивую загрузку любыми способами. Но некоторые обходные пути JS набрали обороты, поскольку не было свойства src, поэтому предварительный анализатор не извлекал изображение и делал это только тогда, когда пользователь действительно отправлял на страницу и срабатывали события. Некоторые браузеры играли с идеей ленивой загрузки самостоятельно, но отпускали, если это не предполагало универсальную согласованность в стандарте. Универсальный стандарт прост, если есть свойство src, браузер будет извлекать элемент без «если» и «но». Представьте, если бы это было не так, бедняга разработчика внешнего интерфейса свалился бы в ад. Так что в глубине души вы поднимаете в дебатах вопрос о функциональности спецификации, как я уже говорил выше. Для этого нет работы. В вашем случае как для экранной, так и для печатной версии дисплея. Как убедиться, что изображения загружаются при отправке команды печати. Ответ прост для печати спецификации постфактум. Отображение фактического экрана, а до факта все, что было до этого, на уровне распространения BOM/DOM. Опять же, вы не можете изменить это. Таким образом, вы должны пойти на компромисс. Компромисс придет в форме другой мыслительной структуры. вместо того, чтобы предполагать, что все готово к печати, сделайте его готовым к печати по команде пользователя. Существует div, который появляется и показывает печатную версию документа, а затем распечатывает оттуда. Пользовательский интерфейс может быть любым, это займет всего секунду или около того, так как большая часть контента будет загружена любыми способами, а отдых займет короткое время. Правила CSS для печати могут быть очень кстати в этом отношении. Вы можете почти увидеть его в действии во многих местах в Интернете. вывод, поскольку мы стоим сегодня, где мы находимся с функциональностью спецификации, объединяющей отображение экрана и отображение печати с ленивой загрузкой, - это не то, для чего была предназначена ленивая загрузка, поэтому не обеспечивает лучшего решения, чем простые хаки. Таким образом, вы должны создать свой пользовательский интерфейс на основе вашего контекста, разделяющего их, чтобы он работал правильно.

@jqueryHtmlCSS для редактирования текста. Очень признателен.

Syed 21.12.2020 12:15

Просто покажи мне свой отвратительный код, я не хочу читать!

Если вы не хотите читать мои бредни, последний раздел «Демо» содержит скрипт, который вы можете исследовать (достаточно хорошо прокомментированный в коде) с инструкциями.

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

Также есть версия, которая почти работает в IE здесь, почему-то экран "подготовка к печати" не исчезает перед печатью, но весь остальной функционал работает (на удивление)!

Что попробовать:

  1. Попробуйте в разных размерах браузера, чтобы увидеть динамический запрос изображения.
  2. попробуйте это на более медленном соединении и проверьте вкладку сети, чтобы увидеть отложенную загрузку в действии и динамические изменения в том, как работает отложенная загрузка в зависимости от скорости соединения.
  3. попробуйте нажать CTRL + P при медленном сетевом соединении (без прокрутки страницы), чтобы увидеть, как мы загружаем изображения, еще не находящиеся в DOM перед печатью.
  4. попробуйте загрузить страницу с медленным сетевым подключением, а затем используйте FILE > PRINT, чтобы увидеть, как мы обрабатываем изображения, которые еще не загружены в этом сценарии.

Версия 0.1, доказательство концепции

Так что впереди еще долгий путь, но я подумал, что пока поделюсь своим решением.

Это сложно (и ошибочно), но это около 90% того, что вы просили, и потенциально лучшее решение, чем ленивая загрузка текущего изображения.

Кроме того, я ужасно не умею писать чистый JS при прототипировании идеи. Я могу только извиниться перед любым из вас, достаточно смелым, чтобы попытаться понять мой код на этом этапе!

проверено только в chrome - так что, как вы понимаете, это может не работать в других браузерах, тем более, что получение содержимого тега <noscript>, как известно, непоследовательно. Однако в конечном итоге я надеюсь, что это будет готовое к производству решение.

Наконец, на этом этапе было слишком много работы по созданию API, поэтому для изменения размера изображения я использовал https://placehold.it — так что там нужно удалить несколько строк избыточного кода.

Ключевые особенности/преимущества

Нет потерянных байтов изображения

Это решение вычисляет фактический размер запрашиваемого изображения. Поэтому вместо того, чтобы добавлять точки останова в нечто вроде элемента <picture>, мы на самом деле говорим, что нам нужно изображение шириной 427 пикселей (например).

Это, очевидно, требует решения для изменения размера изображения на стороне сервера (которое выходит за рамки ответа на переполнение стека), но преимущества огромны.

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

Во-вторых, разница между изображениями шириной 320 пикселей и 400 пикселей с точки зрения КБ составляет более 40%, поэтому выбор изображения «похожего размера» не идеален (что в основном и делает элемент <picture>).

В-третьих, если у людей (таких как я) есть массивные 4K-мониторы и приличная скорость соединения, то вы действительно можете предоставить им изображение 4K (хотя определение скорости соединения — это улучшение, которое мне нужно сделать в версии 0.2).

В-четвертых, что, если изображение имеет ширину 50% своего родительского контейнера при одном размере экрана, 25% ширины родительского контейнера при другом, но ширина контейнера составляет 60% при одном размере экрана и 80% ширины экрана при другом.

Попытка сделать это правильно в элементе <picture> может в лучшем случае разочаровать. Еще хуже, если вы затем решите изменить макет, так как вам придется пересчитывать все проценты ширины и т. д.

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

Минимальная разметка (и семантически правильная разметка)

Хотя вы хотели не использовать <noscript> и избегать атрибутов data, мне нужно было использовать оба.

Однако разметка, которую вы пишете/генерируете, представляет собой буквально элемент <img>, написанный так, как вы обычно заключаете его в тег <noscript>.

Как только изображение полностью загрузится, весь беспорядок будет удален, поэтому в вашем DOM останется только элемент <img>.

Если вы когда-нибудь захотите заменить решение (если технология браузера улучшится и т. д.), то простая замена <noscripts> приведет вас к стандартной HTML-разметке, готовой к улучшению.

WebP

Конечно, это решение запрашивает изображения WebP, если они поддерживаются (все дело в производительности!). На стороне сервера вам нужно будет обработать их соответствующим образом (например, если изображение представляет собой PNG с прозрачностью, вы отправляете его обратно, даже если запрашивается изображение WebP).

Печать

О, это было весело!

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

Итак, что я сделал, так это подумал о реальных сценариях и осветил их как можно изящнее.

  1. Если пользователь подключен к быстрому соединению, мы лениво загружаем изображения, но не ждем прокрутки, чтобы сделать это. Это может означать немного большую нагрузку на наши серверы, но я действую так, как будто печать очень важна (уступает только скорости).
  2. Если у пользователя медленное соединение, мы используем традиционную ленивую загрузку.
  3. Если они нажимают CTRL + P, мы перехватываем команду печати и отображаем сообщение во время загрузки изображений. Эта концепция взята из примера ОП, предоставленного Хусейном Джирдехом, но с использованием нашего механизма ленивой загрузки.
  4. Если пользователь печатает с помощью FILE > PRINT, вместо этого мы отображаем заполнитель для изображений, которые еще не загружены, объясняя, что им нужно прокрутить страницу, чтобы отобразить изображение. (заполнители примерно такого же размера, как и изображение).

Это лучший компромисс, который я мог придумать на данный момент.

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

Не 100% идеальное решение для этого, но поскольку контент «вверху» не должен загружаться лениво, а 95% посещений страниц начинаются с верхней части страницы, это разумный компромисс.

Мы используем пустой SVG (созданный с правильными пропорциями «на лету»), используя data URI в качестве заполнителя для изображения, а затем заменяем src, когда нам нужно загрузить изображение. Это позволяет избежать сетевых запросов и гарантирует отсутствие смещения макета при загрузке изображения.

Это также означает, что страница всегда семантически верна, нет пустых hrefs и т. д.

Сдвиг макета происходит, если пользователь уже прокрутил страницу, а затем перезагрузил ее. Это связано с тем, что элементы <img> создаются с помощью JavaScript (если только JavaScript не отключен, в этом случае изображение отображается из версии <noscript> изображения). Таким образом, они не существуют в DOM, когда он анализируется.

Этого можно избежать, но он требует компромиссов в других местах, поэтому я воспринял это как приемлемый вариант на данный момент.

Работает без JavaScript и чистой разметки

Исходная разметка — это просто изображение внутри тега <noscript>. Нет пользовательской разметки или data-attributes и т. д.

Разметка, с которой я пошел, такова:

<noscript class = "lazy">
    <img src = "https://placehold.it/1500x500" alt = "an image" width = "1500px" height = "500px"/>
</noscript>

Он не становится намного более стандартным и чистым, ему даже не нужны теги class = "lazy", если вы не используете теги <noscript> где-либо еще, это чисто для коллизий.

Вы даже можете опустить атрибуты width и height, если вас не волнует смещение макета, но, поскольку кумулятивное смещение макета (CLS) является Core Web Vital, я бы не рекомендовал его.

Доступность

Изображения — это просто стандартные изображения, а атрибуты alt перенесены.

Я даже добавил дополнительную проверку, что если атрибуты alt пусты или отсутствуют, к изображению добавляется большая красная рамка через класс CSS.

Проблемы / компромиссы

Сдвиг макета, если страница уже прокручена

Как упоминалось ранее, если страница уже прокручена, макет будет сильно смещен, как если бы стандартное изображение было добавлено на страницу без атрибутов width и height.

Доступность

Хотя само решение изображения доступно, экран, который появляется при нажатии CTRL + P, недоступен. Это чистая лень с моей стороны, и ее легко решить, когда появится более окончательное решение.

Однако отсутствие поддержки Internet Explorer (см. ниже) является серьезной проблемой доступности.

IE

ОБНОВЛЯТЬ Здесь есть версия, которая почти работает в IE11. Я изучаю, смогу ли я заставить это работать вплоть до IE9.

Также протестировано в Firefox, Edge и Safari (мобильное), похоже, там работает.

ОРИГИНАЛ Хотя это не тестировалось в Firefox, Safari и т. д., достаточно легко приступить к работе, если возникнут проблемы.

Однако доступ к содержимому тегов <noscript>, как известно, затруднен (а в некоторых версиях невозможен) в IE и других старых браузерах, и поэтому это решение, вероятно, никогда не будет работать в IE.

Это важно, когда речь идет о специальных возможностях, поскольку многие пользователи средств чтения с экрана полагаются на IE, поскольку он хорошо работает с JAWS.

Решение, которое я имею в виду, состоит в том, чтобы использовать обнюхивание агента пользователя на сервере и обслуживать другую разметку и JavaScript, но это сложно и очень узко, поэтому я не собираюсь делать это в этом ответе.

Проверка задержки

Я использую довольно грубый способ проверки задержки (чтобы попытаться угадать, подключен ли кто-то к 3G / 4G-соединению), дважды загружая крошечное изображение и измеряя время загрузки.

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

Это требует полного переосмысления, но на данный момент этого достаточно, пока я работаю над другими частями.

Демо

Не удалось использовать встроенную скрипту из-за ограничения количества символов в 30 000 символов!

Итак, вот текущий JS Fiddle — https://jsfiddle.net/9d5qs6ba/.

В качестве альтернативы, как упоминалось ранее, демонстрацию можно легко просмотреть и протестировать на домене, которым я управляю, по адресу https://inhu.co/so/image-concept.php.

Я знаю, что это не «готовая вещь», связанная с вашими собственными доменами, но сложно протестировать печать на jsfiddle и т. д.

Ответ принят как подходящий

Правильное решение для ленивой загрузки для печати в 2022 году — использовать нативный атрибут loading.

<img loading=lazy>

Рекомендация использовать пользовательскую кнопку печати устарела, так как проблема с хромом 875403 была исправлена.

Предыдущие рекомендации включали добавление пользовательской кнопки печати (что не решило проблему при использовании встроенной функции печати браузера) или использование JavaScript для загрузки изображений onBeforePrint последнее не считается хорошим решением, как loading=lazy, как «DOM- единственное решение, не должно полагаться на JavaScript.

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

Другие вопросы по теме