domDetect.mjs

import get from 'lodash-es/get.js'
import cloneDeep from 'lodash-es/cloneDeep.js'
import ispint from './ispint.mjs'
import evem from './evem.mjs'
import isfun from './isfun.mjs'


// function domDetect(f, opt = {}) {

//使用IntersectionObserver與ResizeObserver時, 當顯隱頻繁導致元素尚未出現需等待與過快清除時, 會導致WTextSelect下拉選單無法穩定出現內容問題

//     let ele = null
//     let obInts = null
//     let obRes = null

//     //tolerancePixel
//     let tolerancePixel = get(opt, 'tolerancePixel', null)
//     if (!ispint(tolerancePixel)) {
//         tolerancePixel = 1
//     }

//     //ev
//     let ev = evem()

//     //timer, sold
//     let timer
//     let sold = {
//         offsetWidth: 0,
//         offsetHeight: 0,
//     }

//     //check
//     if (!isfun(f)) {
//         console.log('invalid f', f)
//         return ev
//     }

//     //check
//     if (!window.IntersectionObserver) {
//         console.log('invalid IntersectionObserver')
//         return ev
//     }

//     //check
//     if (!window.ResizeObserver) {
//         console.log('invalid ResizeObserver')
//         return ev
//     }

//     //setInterval
//     timer = setInterval(() => {

//         //execute
//         let eleTemp = f()

//         //check
//         if (eleTemp) {

//             //save
//             ele = eleTemp

//             //obInts
//             if (!obInts) {
//                 obInts = new window.IntersectionObserver((entries) => {
//                     let entry = entries[0]

//                     setTimeout(() => { //emit觸發事件為同步, 用setTimeout脫勾
//                         ev.emit('display', { mode: entry.isIntersecting ? 'show' : 'hide', ele })
//                     }, 1)

//                 })
//                 obInts.observe(ele)
//             }

//             //obRes
//             if (!obRes) {
//                 obRes = new window.ResizeObserver((entries) => {
//                     // let entry = entries[0]

//                     //new size
//                     let snew = {
//                         offsetWidth: ele.offsetWidth,
//                         offsetHeight: ele.offsetHeight,
//                     }

//                     //save sold
//                     let soldt = { ...sold }

//                     //tolerancePixel
//                     let bw = Math.abs(sold.offsetWidth - snew.offsetWidth) > tolerancePixel
//                     let bh = Math.abs(sold.offsetHeight - snew.offsetHeight) > tolerancePixel

//                     if (bw || bh) {
//                         setTimeout(() => { //emit觸發事件為同步, 用setTimeout脫勾
//                             ev.emit('resize', { sold: soldt, snew, ele })
//                             ev.emit('resizeWithWindow', { sold: soldt, snew, ele, from: 'dom' })
//                         }, 1)
//                     }

//                     //save
//                     sold = snew

//                 })
//                 obRes.observe(ele)
//             }

//             //clearInterval
//             clearInterval(timer)

//         }

//     }, 10)

//     //fWindowResize
//     let fWindowResize = (e) => {
//         ev.emit('resizeWithWindow', { snew: sold, from: 'window' })
//     }
//     window.addEventListener('resize', fWindowResize)

//     //clear
//     ev.clear = () => {
//         obInts.unobserve(ele)
//         obRes.unobserve(ele)
//         clearInterval(timer) //若一直沒偵測到元素故也需要強制中止timer
//         window.removeEventListener('resize', fWindowResize)
//     }

//     return ev
// }


/**
 * 前端監聽DOM元素resize、resizeWithWindow事件,其中resizeWithWindow為dom resize與window resize皆會觸發的事件
 *
 * Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/domDetect.test.mjs Github}
 * @memberOf wsemi
 * @param {Function} f 輸入取得dom函數
 * @param {Integer} [opt.timeInterval=20] 輸入定期偵測時間整數,預設20毫秒
 * @param {Integer} [opt.tolerancePixel=1] 輸入容許誤差整數,單位px,預設1
 * @returns {Object} 回傳物件,可使用on與clear函數,on可監聽resize與resizeWithWindow事件,clear為釋放監聽
 * @example
 * need test in browser
 *
 * //監聽dom
 * let de = domDetect(() => {
 *     return document.querySelector('#id')
 * })
 * de.on('resize', (s) => {
 *     console.log('resize', s)
 * })
 * de.on('resizeWithWindow', (s) => {
 *     console.log('resizeWithWindow', s)
 * })
 * de.on('display', (s) => {
 *     console.log('display', s)
 * })
 *
 * //釋放監聽
 * de.clear()
 *
 */
function domDetect(f, opt = {}) {

    //timeInterval
    let timeInterval = get(opt, 'timeInterval', null)
    if (!ispint(timeInterval)) {
        timeInterval = 20
    }

    //tolerancePixel
    let tolerancePixel = get(opt, 'tolerancePixel', null)
    if (!ispint(tolerancePixel)) {
        tolerancePixel = 1
    }

    //ev
    let ev = evem()

    //timer, st, sd, smode
    let timer
    let st = {
        offsetWidth: 0,
        offsetHeight: 0,
        clientWidth: 0,
        clientHeight: 0,
        windowWidth: 0,
        windowHeight: 0,
    }
    let sd = cloneDeep(st)
    let smode = {
        width: '',
        height: '',
    }

    //check
    if (!isfun(f)) {
        console.log('invalid f', f)
        return null
    }

    //setInterval
    timer = setInterval(() => {

        //execute
        let p = f()

        //check
        if (p) {

            //new size
            let snew = {
                offsetWidth: p.offsetWidth,
                offsetHeight: p.offsetHeight,
                clientWidth: p.clientWidth,
                clientHeight: p.clientHeight,
                windowWidth: window.innerWidth,
                windowHeight: window.innerHeight,
            }

            //dw, dh
            let dw = sd.offsetWidth - snew.offsetWidth
            let dh = sd.offsetHeight - snew.offsetHeight

            //bw, bh
            let bw = Math.abs(dw) > tolerancePixel
            let bh = Math.abs(dh) > tolerancePixel

            //smode
            let mw = ''
            if (dw > 0) {
                mw = 'smaller'
            }
            else if (dw < 0) {
                mw = 'larger'
            }
            let mh = ''
            if (dh > 0) {
                mh = 'smaller'
            }
            else if (dh < 0) {
                mh = 'larger'
            }
            smode = {
                width: mw,
                height: mh,
            }

            //detect
            if (bw || bh) {
                let sold = { ...sd }
                setTimeout(() => { //emit觸發事件為同步, 用setTimeout脫勾

                    //detect resize
                    if (snew.offsetWidth > 0 && snew.offsetHeight > 0) {
                        ev.emit('resize', {
                            sold,
                            snew,
                            smode,
                            ele: p,
                        })
                        ev.emit('resizeWithWindow', {
                            sold,
                            snew,
                            smode,
                            ele: p,
                            from: 'dom',
                        })
                    }

                }, 1)
            }

            //save
            st = sd
            sd = snew

        }


    }, timeInterval)

    //fWindowResize
    let fWindowResize = (e) => {
        ev.emit('resizeWithWindow', {
            sold: st,
            snew: sd,
            smode,
            from: 'window',
        })
    }
    window.addEventListener('resize', fWindowResize)

    //clear
    ev.clear = () => {
        clearInterval(timer)
        window.removeEventListener('resize', fWindowResize)
    }

    return ev
}


export default domDetect