2011-04-22 862 views
13

當鼠標移動到元素上時,我想要獲取相對於元素內容區域左上角的鼠標座標(這是不包括填充的區域,邊框和輪廓)。聽起來很簡單,對吧?我至今是一個非常受歡迎的功能:獲取鼠標相對於元素內容區域的位置

function element_position(e) { 
    var x = 0, y = 0; 
    do { 
     x += e.offsetLeft; 
     y += e.offsetTop; 
    } while (e = e.offsetParent); 
    return { x: x, y: y }; 
} 

而且我相對鼠標位置得到一個元素element

p = element_position(element); 
x = mouseEvent.pageX - p.x; 
y = mouseEvent.pageY - p.y; 

這是不完全正確的。由於offsetLeftoffsetTop是元素的「外部」左上角與其偏移父級的「內部」左上角之間的差異,因此總和位置將跳過層次結構中的邊框和填充處的全部

下面是一個比較,應該(希望)澄清我的意思。

  • 如果我有距離的總和中的元素的「外部」左上角的偏移父母的「內部」左上角之間的位置(,外表面減去內部件;什麼,我現在在做什麼),我得到元素的內容區域的位置,減去偏移層次結構中的所有邊框和填充。
  • 如果我得到元素的'外部'左上角和他們抵消父項的'外部'左上角之間的距離總和(外部減去外部),我得到元素的內容區域的位置,減去所需元素的邊界和填充(關閉,但不是那裏)。
  • 如果我得到元素的「內部」左上角與其父親的內部左上角之間的距離總和(內部減號內部),則獲取元素內容區域的位置。這就是我要的。
+0

我個人更喜歡不將CSS應用於畫布本身,而是將畫布包裹在用CSS裝飾的元素中。帶走很多PITA。 ;) – 2011-04-22 12:49:14

+0

這是真的,我不會在我的畫布元素上應用填充/邊框。只是一個理論問題,真的。 – 2011-04-22 12:52:00

+1

看起來'offsetLeft/Top'類型的獲取元素位置的方法不僅不符合目標元素的邊框和填充的大小,而且也包含所有偏移父元素的邊框和填充。請查看我重寫的問題,看看你是否可以考慮我的情況。 – 2011-04-22 13:30:27

回答

10

下面是一個使用element_position()功能是感知填充和邊框的活生生的例子。我爲您的原始示例添加了一些額外的填充和邊距。

http://jsfiddle.net/Skz8g/4/

要使用它,將光標移動到棕色區域。由此產生的白色區域是實際的畫布內容。棕色是填充,紅色是邊框,依此類推。在這個例子和稍後的例子中,canvas xcanvas y讀數指示相對於畫布內容的光標位置。

下面的代碼爲element_position()

function getNumericStyleProperty(style, prop){ 
    return parseInt(style.getPropertyValue(prop),10) ; 
} 

function element_position(e) { 
    var x = 0, y = 0; 
    var inner = true ; 
    do { 
     x += e.offsetLeft; 
     y += e.offsetTop; 
     var style = getComputedStyle(e,null) ; 
     var borderTop = getNumericStyleProperty(style,"border-top-width") ; 
     var borderLeft = getNumericStyleProperty(style,"border-left-width") ; 
     y += borderTop ; 
     x += borderLeft ; 
     if (inner){ 
      var paddingTop = getNumericStyleProperty(style,"padding-top") ; 
      var paddingLeft = getNumericStyleProperty(style,"padding-left") ; 
      y += paddingTop ; 
      x += paddingLeft ; 
     } 
     inner = false ; 
    } while (e = e.offsetParent); 
    return { x: x, y: y }; 
} 

代碼應在IE9,FF和Chrome正常工作,雖然我注意到它是不完全正確的歌劇。

我最初的傾向是使用類似e.offsetX/Y屬性的東西,因爲它們更接近你想要的,並且不涉及循環嵌套元素。然而,他們的行爲在不同瀏覽器之間變化很大,所以需要一些跨瀏覽器的討論。在活生生的例子是在這裏:

http://jsfiddle.net/xUZAa/6/

應該在所有現代瀏覽器的工作 - 歌劇,FF,Chrome瀏覽器,IE9。我個人比較喜歡它,但認爲儘管你最初的問題只是「讓鼠標位置相對於元素的內容區域而言」,但你確實在問如何使element_position()函數正常工作。

+1

感謝您的回答,並感謝所有人的回答。我特別接受你的,因爲它不使用外部庫,並選擇你的Aleadam's,因爲你去創建代碼和演示的麻煩(雖然Aleadam,你是完全正確的,所以感謝你的答案!)。這項任務似乎很簡單,但必須手動實施;我真的認爲,理想情況下,在規範中至少應該有本地屬性,比如'offsetLeft'和'offsetTop',但對於內容區域位置。 – 2011-04-25 13:45:54

+0

我注意到,如果我把光標放在它註冊的畫布的左上角(2,1)不是(0,0)。我試過一個單獨的方法,並運行相同的問題([這裏])(http://stackoverflow.com/questions/29607924/click-in-canvas-is-three-pixels-off)) - 有沒有人有修復? – 2015-04-13 15:16:38

+1

@Pi,只是一個瘋狂的猜測,但它可能取決於光標圖標的「來源」是什麼。原點可能不在您期望的位置,即在箭頭的頂端。您可能想嘗試使用十字光標並查看您獲得的結果。 – brainjam 2015-04-13 16:26:06

1

我不知道這是否是最好的方式,還是最有效利用資源......

不過,我會建議得到X/Y的canvas標籤,邊框寬度,並填充和使用他們一起作爲抵消。

編輯:

使用offsetLeft和的offsetTop

參考:How to Use the Canvas and Draw Elements in HTML5

var x; 
var y; 
if (e.pageX || e.pageY) { 
    x = e.pageX; 
    y = e.pageY; 
} 
else { 
    x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 
    y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 
} 
x -= gCanvasElement.offsetLeft; 
y -= gCanvasElement.offsetTop; 
+0

這是個好主意,但*我該怎麼做? – 2011-04-22 12:36:03

+0

用我在http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-找到的一段代碼更新了答案。元素由N4ppeL - 使用offsetLeft和offsetTop – 2011-04-22 12:44:32

+0

請檢查我完全重寫的問題,讓我知道你是否可以衡量的情況。 ;) – 2011-04-22 13:28:55

2

在你element_position(e)功能,通過使用parentNode層次遍歷,得到填充,偏移和使用getComputedStyle(e, null).getPropertyValue(each_css)邊界,並在返回之前將它們總計爲您的xy值。

有一個帖子提出的樣式跨瀏覽器閱讀此:使用jQuery

http://bytes.com/topic/javascript/answers/796275-get-div-padding

4

function posRelativeToElement(elem, ev){ 
    var $elem = $(elem), 
     ePos = $elem.offset(), 
    mousePos = {x: ev.pageX, y: ev.pageY}; 

    mousePos.x -= ePos.left + parseInt($elem.css('paddingLeft')) + parseInt($elem.css('borderLeftWidth')); 
    mousePos.y -= ePos.top + parseInt($elem.css('paddingTop')) + parseInt($elem.css('borderTopWidth')); 

    return mousePos; 
}; 

活生生的例子:http://jsfiddle.net/vGKM3/

這一現象的根本是簡單:計算元素相對於文檔的位置。然後,我放棄頂部&左填充&邊框(邊距包括在基本定位計算中)。這樣做的內部jQuery代碼基於getComputedStyleelement.currentStyle。不幸的是,我不認爲還有另一種方式......

jQuery的.offset()功能,其位置相對元素的到達文件的核心:

if ("getBoundingClientRect" in document.documentElement) { 
    ... 
    try { 
     box = elem.getBoundingClientRect(); 
    } catch(e) {} 

    var body = doc.body, 
     win = getWindow(doc), 
     clientTop = docElem.clientTop || body.clientTop || 0, 
     clientLeft = docElem.clientLeft || body.clientLeft || 0, 
     scrollTop = (win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop), 
     scrollLeft = (win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft), 
     top = box.top + scrollTop - clientTop, 
     left = box.left + scrollLeft - clientLeft; 

    return { top: top, left: left }; 
}else{ 
    // calculate recursively based on .parentNode and computed styles 
} 

從理論上說,另一種方式來做到這一點是,使用上面的定位代碼:

  • 確保您的元素有position: relative(或絕對)設置
  • 追加了新的元素與position: absolute; top:0px; left:0px;
  • 獲取相對於文檔的新元素元素的位置。這將是同父的內容位置
  • 刪除新元素
+0

非常好的...... – user960567 2012-05-11 15:31:36

相關問題