Я ищу правильный способ реализовать ленивую загрузку изображений без ущерба для печати и доступности, а также без изменения макета (перехода содержимого), предпочтительно с использованием родного 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
, но это, вероятно, должно было быть опробовано раньше, компромисс будет заключаться в том, что как только скрипт отложенной загрузки начнет действовать на элементы изображения, браузер, возможно, уже начал загрузку исходные файлы.
@graham-ritchie Я добавил ссылки на примеры частичных решений.
Итак, я полагаю, что последний вопрос заключается в том, где вы готовы пойти на компромисс. Например, пример с Хусейном Джирде работает, только если вы нажмете его кнопку печати (после чего он просто ждет, пока все изображения загрузятся, пока не позвонит print()
, поэтому при медленном соединении страница может зависнуть на несколько секунд и более). Печать важнее для вас, чем сохранение пропускной способности? Мы предполагаем, что люди не будут печатать на мобильных устройствах? Я не думаю, что мы можем создать идеальное решение, но если мы сделаем определенные предположения, я думаю, мы сможем приблизиться к этому.
И последний вопрос: можем ли мы предположить, что это также серверная сторона (а также клиентская сторона), так как в противном случае мы будем изо всех сил пытаться получить пропорции изображения, чтобы выделить пространство, чтобы гарантировать отсутствие сдвига макета, способствующего CLS.
Спасибо @graham-ritchie, я посмотрю
См. также этот вопрос whatwg о проблеме с использованием встроенной ленивой загрузки и печати: github.com/whatwg/html/issues/6581
Подведение итогов на конец 2021 года о состоянии ленивой загрузки для печати: dev.to/ingosteinke/printable-lazy-loading-3hb6
Обновление от 20 января 2022 г.: изображение для печати с отложенной загрузкой должно было быть исправлено в последней версии Chrome canary, см. bugs.chromium.org/p/chromium/issues/detail?id=875403#c36
@Ingo Steinke Прежде чем перейти к ответам на поднятые вами проблемы, нужно вернуться назад и подумать о том, почему возникла ленивая загрузка и какой ущерб она решила при инициации в качестве основы мысли. Ключевое слово структура мысли ... это не решение, и я бы сказал, что это никогда не было решением, а рамками мысли. Почему мы этого хотели:
Есть один вопрос, который напрашивается, и вы коснулись его в своем предлоге .... ни один из них не контролирует и не изменяет собственные функции браузера. Браузер может получить элемент еще до того, как ваш скрипт будет иметь какое-либо отношение к чему-либо.
Это главная проблема здесь. BOM не заботится и даже не хочет заботиться о том, что ваш скрипт просит сделать все, что он знает, если есть свойство src для извлечения содержимого. Ни одно из решений не изменило это. Если бы мы могли изменить эту функциональность, то мыслительная структура стала бы решением.
Я по-прежнему считаю, что браузеры не должны менять это просто так, и поэтому никогда не получали отслеживания в дебатах. Что сделали браузеры, так это предварительную выборку, известную как спекулятивный или упреждающий предварительный анализ. Это единственное самое большое улучшение в браузерах, которое заслуживает похвалы. Точно так же, как мы вводим URL-адрес в адресной строке при каждом изменении строки, браузер предварительно извлекает содержимое, даже если я не набрал весь URL-адрес. Я специально разработал программу, в которой я перехватывал все, что было получено на сервере от этих предварительных парсеров. В большинстве случаев для получения ответа требуется меньше секунды, и браузеры начинают обрабатывать все это, включая изображения и JS. Это противоречило рывкам с задержкой в эластичном положении лежа, как обсуждалось в № 1 и № 2. Однако это не уменьшило попадание на сервер. Причина, по которой мы делаем ленивую загрузку любыми способами. Но некоторые обходные пути JS набрали обороты, поскольку не было свойства src, поэтому предварительный анализатор не извлекал изображение и делал это только тогда, когда пользователь действительно отправлял на страницу и срабатывали события. Некоторые браузеры играли с идеей ленивой загрузки самостоятельно, но отпускали, если это не предполагало универсальную согласованность в стандарте. Универсальный стандарт прост, если есть свойство src, браузер будет извлекать элемент без «если» и «но». Представьте, если бы это было не так, бедняга разработчика внешнего интерфейса свалился бы в ад. Так что в глубине души вы поднимаете в дебатах вопрос о функциональности спецификации, как я уже говорил выше. Для этого нет работы. В вашем случае как для экранной, так и для печатной версии дисплея. Как убедиться, что изображения загружаются при отправке команды печати. Ответ прост для печати спецификации постфактум. Отображение фактического экрана, а до факта все, что было до этого, на уровне распространения BOM/DOM. Опять же, вы не можете изменить это. Таким образом, вы должны пойти на компромисс. Компромисс придет в форме другой мыслительной структуры. вместо того, чтобы предполагать, что все готово к печати, сделайте его готовым к печати по команде пользователя. Существует div, который появляется и показывает печатную версию документа, а затем распечатывает оттуда. Пользовательский интерфейс может быть любым, это займет всего секунду или около того, так как большая часть контента будет загружена любыми способами, а отдых займет короткое время. Правила CSS для печати могут быть очень кстати в этом отношении. Вы можете почти увидеть его в действии во многих местах в Интернете. вывод, поскольку мы стоим сегодня, где мы находимся с функциональностью спецификации, объединяющей отображение экрана и отображение печати с ленивой загрузкой, - это не то, для чего была предназначена ленивая загрузка, поэтому не обеспечивает лучшего решения, чем простые хаки. Таким образом, вы должны создать свой пользовательский интерфейс на основе вашего контекста, разделяющего их, чтобы он работал правильно.
@jqueryHtmlCSS для редактирования текста. Очень признателен.
Если вы не хотите читать мои бредни, последний раздел «Демо» содержит скрипт, который вы можете исследовать (достаточно хорошо прокомментированный в коде) с инструкциями.
Или есть ссылка на демонстрацию домена, которым я управляю здесь, с которым легче протестировать, если вы хотите его использовать.
Также есть версия, которая почти работает в IE здесь, почему-то экран "подготовка к печати" не исчезает перед печатью, но весь остальной функционал работает (на удивление)!
Что попробовать:
FILE > PRINT
, чтобы увидеть, как мы обрабатываем изображения, которые еще не загружены в этом сценарии.Так что впереди еще долгий путь, но я подумал, что пока поделюсь своим решением.
Это сложно (и ошибочно), но это около 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, если они поддерживаются (все дело в производительности!). На стороне сервера вам нужно будет обработать их соответствующим образом (например, если изображение представляет собой PNG с прозрачностью, вы отправляете его обратно, даже если запрашивается изображение WebP).
О, это было весело!
Мы ничего не можем сделать, если мы отправляем документ на печать, а изображение еще не загружено, я пробовал всевозможные хаки (например, установку фоновых изображений), но это просто невозможно (или я недостаточно умен, чтобы работать вышло....скорее всего!)
Итак, что я сделал, так это подумал о реальных сценариях и осветил их как можно изящнее.
FILE > PRINT
, вместо этого мы отображаем заполнитель для изображений, которые еще не загружены, объясняя, что им нужно прокрутить страницу, чтобы отобразить изображение. (заполнители примерно такого же размера, как и изображение).Это лучший компромисс, который я мог придумать на данный момент.
Не 100% идеальное решение для этого, но поскольку контент «вверху» не должен загружаться лениво, а 95% посещений страниц начинаются с верхней части страницы, это разумный компромисс.
Мы используем пустой SVG (созданный с правильными пропорциями «на лету»), используя data
URI в качестве заполнителя для изображения, а затем заменяем src
, когда нам нужно загрузить изображение. Это позволяет избежать сетевых запросов и гарантирует отсутствие смещения макета при загрузке изображения.
Это также означает, что страница всегда семантически верна, нет пустых hrefs
и т. д.
Сдвиг макета происходит, если пользователь уже прокрутил страницу, а затем перезагрузил ее. Это связано с тем, что элементы <img>
создаются с помощью JavaScript (если только JavaScript не отключен, в этом случае изображение отображается из версии <noscript>
изображения). Таким образом, они не существуют в DOM, когда он анализируется.
Этого можно избежать, но он требует компромиссов в других местах, поэтому я воспринял это как приемлемый вариант на данный момент.
Исходная разметка — это просто изображение внутри тега <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 (см. ниже) является серьезной проблемой доступности.
ОБНОВЛЯТЬ Здесь есть версия, которая почти работает в 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.
Имейте в виду, что даже после исправления ошибки некоторые из ваших пользователей могут по-прежнему посещать ваш сайт с ошибочной версией браузера.
Невозможно выбрать каждый элемент в вашем списке белья и избежать тегов
<noscript>
, вам нужен запасной вариант на случай сбоя JS. Вы хотите показать нам, как вы пытались решить эти проблемы и где вы боретесь, чтобы мы могли помочь, поскольку на данный момент это слишком близко к вопросу «как я?» / основанному на мнении, я думаю, что вопрос имеет много заслуг, просто нужно немного больше внимания.