Repository navigation
fix: make image modal keyboard accessible #1260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
chinmayee5599
wants to merge
6
commits into
layer5io:master
from
chinmayee5599:fix/image-modal-keyboard
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
731100d
fix: preserve image rendering and accessibility
chinmayee5599 f6efa89
fix: preserve image alt text in modal
chinmayee5599 8876cce
fix: preserve image modal behavior
chinmayee5599 6484cc2
fix: resolve image modal review issues
chinmayee5599 a7a73ca
fix: address image modal review feedback
chinmayee5599 0326d15
Merge branch 'master' into fix/image-modal-keyboard
chinmayee5599 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,61 +1,159 @@ | ||
| <div id="myModal" class="modal"> | ||
| <button class="modal-close" onclick="closeModal()">close</button> | ||
| <div | ||
| id="myModal" | ||
| class="modal" | ||
| role="dialog" | ||
| aria-modal="true" | ||
| aria-label="Image preview" | ||
| > | ||
| <button | ||
| type="button" | ||
| class="modal-close" | ||
| onclick="closeModal()" | ||
| aria-label="Close image" | ||
| > | ||
| close | ||
| </button> | ||
|
|
||
| <div class="modal-cont"> | ||
| <img class="modal-pic" id="modalPic" onclick="closeModal()" style="max-width: 100%; max-height: 80vh; margin: auto;" alt="Modal-pic"> | ||
| <img | ||
| class="modal-pic" | ||
| id="modalPic" | ||
| onclick="closeModal()" | ||
| style="max-width: 100%; max-height: 80vh; margin: auto;" | ||
| alt="Image preview" | ||
| > | ||
| </div> | ||
| </div> | ||
|
|
||
| <script> | ||
| // Open the Modal | ||
| function openModal(imageIdOrElement) { | ||
| var src; | ||
| var modalTrigger = null; | ||
| var modalFallbackFocus = null; | ||
|
|
||
| if (typeof imageIdOrElement === 'string') { | ||
| // If it's a string (image ID), get the source using the ID | ||
| src = document.getElementById(imageIdOrElement).src; | ||
| } else if (imageIdOrElement instanceof HTMLImageElement) { | ||
| // If <img>, get the source directly. currentSrc is what the browser actually | ||
| // chose, so inside a <picture> the modal reuses the already-fetched WebP | ||
| // rather than downloading the larger original. | ||
| src = imageIdOrElement.currentSrc || imageIdOrElement.src; | ||
| function openModal(image) { | ||
| if (!(image instanceof HTMLImageElement)) { | ||
| return; | ||
| } | ||
|
|
||
| var src = image.currentSrc || image.src; | ||
|
|
||
| if (src && src.includes("#")) { | ||
| src = src.substring(0, src.indexOf("#")); | ||
| } | ||
|
|
||
| document.getElementById("modalPic").src = src; | ||
| document.getElementById("myModal").style.display = "block"; | ||
| var modal = document.getElementById("myModal"); | ||
| var modalPic = document.getElementById("modalPic"); | ||
| var closeButton = modal.querySelector(".modal-close"); | ||
|
|
||
| modalTrigger = image.closest(".md__image-trigger"); | ||
| modalFallbackFocus = document.activeElement; | ||
|
|
||
| modalPic.src = src; | ||
| modalPic.alt = image.alt || "Image preview"; | ||
| modal.style.display = "block"; | ||
|
|
||
| if (closeButton) { | ||
| closeButton.focus(); | ||
| } | ||
| } | ||
|
|
||
| function closeModal() { | ||
| var modal = document.getElementById("myModal"); | ||
| var modalPic = document.getElementById("modalPic"); | ||
| var trigger = modalTrigger; | ||
| var fallbackFocus = modalFallbackFocus; | ||
|
|
||
| modalPic.src = ""; | ||
| modal.style.display = "none"; | ||
| modalTrigger = null; | ||
| modalFallbackFocus = null; | ||
|
|
||
| if (trigger && document.contains(trigger)) { | ||
| trigger.focus(); | ||
| } else if (fallbackFocus && document.contains(fallbackFocus)) { | ||
| fallbackFocus.focus(); | ||
| } | ||
| } | ||
|
|
||
| function isModalOpen() { | ||
| return document.getElementById("myModal").style.display === "block"; | ||
| } | ||
|
|
||
| // Close the Modal | ||
| function closeModal() { | ||
| document.getElementById("modalPic").src = ""; | ||
| document.getElementById("myModal").style.display = "none"; | ||
| function trapModalFocus(event) { | ||
| if (event.key !== "Tab" || !isModalOpen()) { | ||
| return; | ||
| } | ||
|
|
||
| var modal = document.getElementById("myModal"); | ||
|
|
||
| var focusableElements = modal.querySelectorAll( | ||
| 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])' | ||
| ); | ||
|
|
||
| if (!focusableElements.length) { | ||
| event.preventDefault(); | ||
| return; | ||
| } | ||
|
|
||
| var firstElement = focusableElements[0]; | ||
| var lastElement = focusableElements[focusableElements.length - 1]; | ||
|
|
||
| if (event.shiftKey && document.activeElement === firstElement) { | ||
| event.preventDefault(); | ||
| lastElement.focus(); | ||
| } else if (!event.shiftKey && document.activeElement === lastElement) { | ||
| event.preventDefault(); | ||
| firstElement.focus(); | ||
| } | ||
| } | ||
|
|
||
| // Listen for the Escape key to close the modal | ||
| document.addEventListener("keydown", function (event) { | ||
| if (event.key === "Escape") { | ||
| if (event.key === "Escape" && isModalOpen()) { | ||
| event.preventDefault(); | ||
| closeModal(); | ||
| return; | ||
| } | ||
|
|
||
| trapModalFocus(event); | ||
| }); | ||
|
|
||
| // Attach onclick attribute to all <img> after DOM is loaded | ||
| document.addEventListener("DOMContentLoaded", function () { | ||
| var imgTags = document.querySelectorAll("img"); | ||
| imgTags.forEach(function (img) { | ||
| img.onclick = function () { | ||
| if (img.dataset.modal !== "false") { | ||
| openModal(img); | ||
| var modal = document.getElementById("myModal"); | ||
|
|
||
| var imageTriggers = document.querySelectorAll(".md__image-trigger"); | ||
|
|
||
| imageTriggers.forEach(function (trigger) { | ||
| var link = trigger.closest("a"); | ||
| var image = trigger.querySelector("img"); | ||
|
|
||
| if (link && image) { | ||
| image.dataset.modal = "false"; | ||
| trigger.replaceWith(image); | ||
| return; | ||
| } | ||
|
|
||
| trigger.addEventListener("click", function () { | ||
| if (image) { | ||
| openModal(image); | ||
| } | ||
| }; | ||
| }); | ||
| }); | ||
|
|
||
| // Close modal when clicking anywhere inside #myModal | ||
| var modal = document.getElementById("myModal"); | ||
| modal.addEventListener("click", function () { | ||
| closeModal(); | ||
| document.querySelectorAll("img:not(.md__image-trigger img)").forEach(function (img) { | ||
| if (img.dataset.modal === "false" || img.closest("#myModal")) { | ||
| return; | ||
| } | ||
|
|
||
| img.addEventListener("click", function () { | ||
| openModal(img); | ||
| }); | ||
| }); | ||
|
|
||
| if (modal) { | ||
| modal.addEventListener("click", function (event) { | ||
| if (event.target === modal) { | ||
| closeModal(); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| </script> | ||
| </script> |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.