天天看點

document.elementFromPoint

先說一下這個方法的參數

elemntFromPoint(x,y);//傳入坐标值,傳回目前頁面上包含該坐标點的頂層元素           

注意2點,坐标值和頂層元素

先說坐标,因為不同的人了解是不一樣的,也就造就了這個方法在不同的浏覽器中表現是不一樣的,是以在傳入坐标時就分 整體頁面坐标 和 可視區域坐标,我們看上篇文章中的圖來了解下:

document.elementFromPoint

中間的方塊是可視區域,紅點相對可視區域的左上角我們稱之為 clientX和clientY,相對于頁面起始處的左上角稱之為 pageX和pageY

有的浏覽器在調用elementFromPoint時要求傳入clientX和clientY而有的要求pageX和pageY,具體的詳見:

http://www.quirksmode.org/webkit.html

看 elementFromPoint部分,上面也提到如果在标準中進行規則統一,也會規定使用可視區域的坐标

顯然我們如果要用這個方法時,就要注意相容了,而對于同一種浏覽器,因為版本的不同,導緻同樣的這個方法可能要求傳入的坐标也不同,比如chrome浏覽器,那麼,我們該如何去相容呢?

The

w3c specification

says:

The elementFromPoint(x, y) method, when invoked, must return the element at coordinates x,y in the viewport. The element to be returned is determined through hit testing. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll bar (if any), or y is greater than the viewport height excluding the size of a rendered scroll bar (if any), the method must return null. If there is no element at the given position the method must return the root element, if any, or null otherwise.

調用elementFromPoint時,必須傳入可視區域内的坐标,如果你傳入的坐标不在可視區域内,即使在這個坐标處有元素,也将傳回null;我們可以根據這個特性來寫相容代碼:

使用jq實作的代碼如下,我們可以很友善的改寫成自已的

(function($) {
    var check = false,
        isRelative = true;
    $.elementFromPoint = function(x, y) {
        if (!document.elementFromPoint) return null;
        if (!check) {
            var sl;
            if ((sl = $(document).scrollTop()) > 0) {
                isRelative = (document.elementFromPoint(0, sl + $(window).height() - 1) == null);
            } else if ((sl = $(document).scrollLeft()) > 0) {
                isRelative = (document.elementFromPoint(sl + $(window).width() - 1, 0) == null);
            }
            check = (sl > 0);
        }
        if (!isRelative) {
            x += $(document).scrollLeft();
            y += $(document).scrollTop();
        }
        return document.elementFromPoint(x, y);
    }
})(jQuery);           

原理就是上面說的,如果頁面有滾動,則嘗試擷取頁面坐标最邊緣處的元素,如果能擷取到,說明是使用頁面坐标,因為在有滾動的情況下,擷取可視邊緣處的坐标,頁面坐标會大于可視區域的坐标,是以如果是用可視區域坐标,肯定傳回null

嗯,一切看起來都還不錯,IE6 7就不行了,仔細看了下這個方法,這個方法最早應該是IE特有的,最後被其它浏覽器實作,在IE下,一直使用的是可視區域的坐标,但是在IE6 7的情況下,當你傳大于可視區域的坐标時,也是可以擷取到值的,也就造成了上面相容代碼無法在IE6 7下正常工作,是以上面的代碼并沒有考慮IE6 7的情況,需要你添加一些判斷。

除了坐标問題外,該方法隻能傳回頂層元素,也就是在有2個元素重疊的情況下,隻能傳回最上層的元素。

之前我也有寫過高效拖動的文章,比如拖動排序分類,常見的是滑鼠在拖動的時候,不停的計算滑鼠是在哪個分類上面,然後做出變換的效果,如果清單元素比較少的情況下還是可以的,如果大于1000個,而這些分類的高度不定,通過這個循環判斷的方法顯示就會覺得很卡

我在之前的文章中提到可以在mousemove時,不讓任何元素擋着滑鼠(通常我們可能會在拖動時,讓被拖動的元素随滑鼠一起移動,這時候元素可能擋在滑鼠的下方),可通過事件的event.target擷取滑鼠指向的元素,這樣清單中有多少個元素都不會影響到效果

回頭說一下setCapture方法,該方法是IE下讓元素捕獲到事件,比如滑鼠移到浏覽器外邊也可以響應得到,然而這個方法在firefox在4版本時被引進firefox中,引入後如果拖動時你調用了setCapture,就算滑鼠下面沒有東西擋着,event.target也始終是滑鼠按下時的target,IE中則不是,調用setCapture後,mousemove時依然可以擷取到,當然IE下可能要用event.srcElement擷取

是以,保險起見,我們要調用setCapture,而調用後,firefox4以後又不能及時擷取到滑鼠下的元素,抛開這些,在ios裝置上,touchmouse時,同樣也是擷取不到手指指向的元素,是以隻好回頭折騰這個elementFromPoint了。

在折騰這些的時候,還發現諸如getBoundingClientRect在ios裝置上也是有問題的,這個方法正常傳回相對可視區域的坐标,而在ios上,實作的卻是相對頁面的坐标,實在讓人郁悶

回頭再看一下相容,之前做浏覽器間的相容,更多的是這個浏覽器有這個方法,那個浏覽器有那個方法,方法的不同而已,現在的相容則是同樣的方法,最終的結果不同,比如setCapture還有getBoundingClientRect等,可能還有其它一些有問題的方法,這種相容起來更麻煩,遠不如沒有方法來的幹脆些。

繼續閱讀