refactor: better event listener cleanup (#279)

* refactor: change hires loader function name

* feat: add loading transition animation and improve performance

* refactor: refactor gallery creation and update functions

* feat: create createDivWithClass utility function

* feat: refactor abort signal handling in event listener and promise chain

- Add functionality to set up an abort controller for cleanup
- Add an event listener to abort the controller when necessary
- Modify event listener to include the abort signal
- Modify promise chain to include the abort signal
This commit is contained in:
Spedon
2024-02-11 14:22:48 +08:00
committed by GitHub
parent 997207fa90
commit c84b4cf234
3 changed files with 89 additions and 75 deletions

View File

@@ -344,6 +344,10 @@ function lores(imgs: DesktopImage[]): void {
function setLoaderForHiresImage(e: HTMLImageElement): void { function setLoaderForHiresImage(e: HTMLImageElement): void {
if (!e.complete) { if (!e.complete) {
isLoading.set(true) isLoading.set(true)
// abort controller for cleanup
const controller = new AbortController()
const abortSignal = controller.signal
// event listeners
e.addEventListener( e.addEventListener(
'load', 'load',
() => { () => {
@@ -355,8 +359,11 @@ function setLoaderForHiresImage(e: HTMLImageElement): void {
.catch((e) => { .catch((e) => {
console.log(e) console.log(e)
}) })
.finally(() => {
controller.abort()
})
}, },
{ once: true, passive: true } { once: true, passive: true, signal: abortSignal }
) )
e.addEventListener( e.addEventListener(
'error', 'error',
@@ -369,8 +376,11 @@ function setLoaderForHiresImage(e: HTMLImageElement): void {
.catch((e) => { .catch((e) => {
console.log(e) console.log(e)
}) })
.finally(() => {
controller.abort()
})
}, },
{ once: true, passive: true } { once: true, passive: true, signal: abortSignal }
) )
} else { } else {
_gsap _gsap

View File

@@ -32,6 +32,13 @@ export function removeDuplicates<T>(arr: T[]): T[] {
return [...new Set(arr)] return [...new Set(arr)]
} }
export function createDivWithClass(className: string): HTMLDivElement {
const div = document.createElement('div')
if (className === '') return div // optimization
div.classList.add(className)
return div
}
/** /**
* custom "reactive" object * custom "reactive" object
*/ */

View File

@@ -3,7 +3,7 @@ import { type Swiper } from 'swiper'
import { container, scrollable } from '../container' import { container, scrollable } from '../container'
import { isAnimating, navigateVector, setIndex, state } from '../globalState' import { isAnimating, navigateVector, setIndex, state } from '../globalState'
import { expand, loadGsap, removeDuplicates } from '../globalUtils' import { createDivWithClass, expand, loadGsap, removeDuplicates } from '../globalUtils'
import { type ImageJSON } from '../resources' import { type ImageJSON } from '../resources'
import { mounted } from './state' import { mounted } from './state'
@@ -14,9 +14,11 @@ import { capitalizeFirstLetter, loadSwiper, type MobileImage } from './utils'
* variables * variables
*/ */
let swiperNode: HTMLDivElement let galleryInner: HTMLDivElement
let gallery: HTMLDivElement let gallery: HTMLDivElement
let curtain: HTMLDivElement let curtain: HTMLDivElement
let indexDiv: HTMLDivElement
let navDiv: HTMLDivElement
let indexDispNums: HTMLSpanElement[] = [] let indexDispNums: HTMLSpanElement[] = []
let galleryImages: MobileImage[] = [] let galleryImages: MobileImage[] = []
let collectionImages: MobileImage[] = [] let collectionImages: MobileImage[] = []
@@ -89,14 +91,11 @@ function slideDown(): void {
export function initGallery(ijs: ImageJSON[]): void { export function initGallery(ijs: ImageJSON[]): void {
// create gallery // create gallery
createGallery(ijs) constructGallery(ijs)
// get elements // get elements
indexDispNums = Array.from( indexDispNums = Array.from(
document.getElementsByClassName('nav').item(0)?.getElementsByClassName('num') ?? [] indexDiv.getElementsByClassName('num') ?? []
) as HTMLSpanElement[] ) as HTMLSpanElement[]
swiperNode = document.getElementsByClassName('galleryInner').item(0) as HTMLDivElement
gallery = document.getElementsByClassName('gallery').item(0) as HTMLDivElement
curtain = document.getElementsByClassName('curtain').item(0) as HTMLDivElement
galleryImages = Array.from(gallery.getElementsByTagName('img')) as MobileImage[] galleryImages = Array.from(gallery.getElementsByTagName('img')) as MobileImage[]
collectionImages = Array.from( collectionImages = Array.from(
document document
@@ -137,7 +136,7 @@ export function initGallery(ijs: ImageJSON[]): void {
}) })
loadSwiper() loadSwiper()
.then((S) => { .then((S) => {
_swiper = new S(swiperNode, { spaceBetween: 20 }) _swiper = new S(galleryInner, { spaceBetween: 20 })
_swiper.on('slideChange', ({ realIndex }) => { _swiper.on('slideChange', ({ realIndex }) => {
setIndex(realIndex) setIndex(realIndex)
}) })
@@ -204,32 +203,47 @@ function galleryLoadImages(): void {
}) })
} }
function createGallery(ijs: ImageJSON[]): void { function constructGalleryNav(): void {
/** // index
* gallery indexDiv = document.createElement('div')
* |- galleryInner indexDiv.insertAdjacentHTML(
* |- swiper-wrapper 'afterbegin',
* |- swiper-slide `<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>
* |- img <span>/</span>
* |- swiper-slide <span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>`
* |- img )
* |- ... // close
* |- nav const closeDiv = document.createElement('div')
* |- index closeDiv.innerText = capitalizeFirstLetter(container.dataset.close)
* |- close closeDiv.addEventListener(
*/ 'click',
() => {
slideDown()
},
{ passive: true }
)
closeDiv.addEventListener(
'keydown',
() => {
slideDown()
},
{ passive: true }
)
// nav
navDiv = createDivWithClass('nav')
navDiv.append(indexDiv, closeDiv)
}
function constructGalleryInner(ijs: ImageJSON[]): void {
// swiper wrapper // swiper wrapper
const _swiperWrapper = document.createElement('div') const swiperWrapper = createDivWithClass('swiper-wrapper')
_swiperWrapper.className = 'swiper-wrapper'
// loading text // loading text
const loadingText = container.dataset.loading + '...' const loadingText = container.dataset.loading + '...'
for (const ij of ijs) { for (const ij of ijs) {
// swiper slide // swiper slide
const _swiperSlide = document.createElement('div') const swiperSlide = createDivWithClass('swiper-slide')
_swiperSlide.className = 'swiper-slide'
// loading indicator // loading indicator
const l = document.createElement('div') const l = createDivWithClass('loadingText')
l.className = 'loadingText'
l.innerText = loadingText l.innerText = loadingText
// img // img
const e = document.createElement('img') as MobileImage const e = document.createElement('img') as MobileImage
@@ -253,63 +267,46 @@ function createGallery(ijs: ImageJSON[]): void {
{ once: true, passive: true } { once: true, passive: true }
) )
// parent container // parent container
const p = document.createElement('div') const p = createDivWithClass('slideContainer')
p.className = 'slideContainer'
// append // append
p.append(e) p.append(e, l)
p.append(l) swiperSlide.append(p)
_swiperSlide.append(p) swiperWrapper.append(swiperSlide)
_swiperWrapper.append(_swiperSlide)
} }
// swiper node // swiper node
const _swiperNode = document.createElement('div') galleryInner = createDivWithClass('galleryInner')
_swiperNode.className = 'galleryInner' galleryInner.append(swiperWrapper)
_swiperNode.append(_swiperWrapper) }
// index
const _index = document.createElement('div') function constructGallery(ijs: ImageJSON[]): void {
_index.insertAdjacentHTML( /**
'afterbegin', * gallery
`<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span> * |- galleryInner
<span>/</span> * |- swiper-wrapper
<span class="num"></span><span class="num"></span><span class="num"></span><span class="num"></span>` * |- swiper-slide
) * |- img
// close * |- swiper-slide
const _close = document.createElement('div') * |- img
_close.innerText = capitalizeFirstLetter(container.dataset.close) * |- ...
_close.addEventListener( * |- nav
'click', * |- index
() => { * |- close
slideDown() */
},
{ passive: true }
)
_close.addEventListener(
'keydown',
() => {
slideDown()
},
{ passive: true }
)
// nav
const _navDiv = document.createElement('div')
_navDiv.className = 'nav'
_navDiv.append(_index, _close)
// gallery // gallery
const _gallery = document.createElement('div') gallery = createDivWithClass('gallery')
_gallery.className = 'gallery' constructGalleryInner(ijs)
_gallery.append(_swiperNode) constructGalleryNav()
_gallery.append(_navDiv) gallery.append(galleryInner, navDiv)
/** /**
* curtain * curtain
*/ */
const _curtain = document.createElement('div') curtain = createDivWithClass('curtain')
_curtain.className = 'curtain'
/** /**
* container * container
* |- gallery * |- gallery
* |- curtain * |- curtain
*/ */
container.append(_gallery, _curtain) container.append(gallery, curtain)
} }