简体   繁体   English

获取相对于元素的内容区域的鼠标位置

[英]getting mouse position relative to content area of an element

When the mouse is moved over an element, I want to get the mouse coordinates of the cursor relative to the top-left of the element's content area (this is the area excluding padding, border and outline). 当鼠标在元素上移动时,我想获得光标相对于元素内容区域左上角的鼠标坐标(这是不包括填充,边框和轮廓的区域)。 Sounds simple, right? 听起来很简单吧? What I have so far is a very popular function: 到目前为止我所拥有的是一个非常受欢迎的功能:

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 };
}

And I'd get the mouse position relative to an element element with: 并且我将获得相对于元素element的鼠标位置:

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

That isn't quite correct. 这不太正确。 Because the offsetLeft and offsetTop are the differences between the 'outer' top left of an element and the 'inner' top left of its offset parent, the sum position will skip over all borders and paddings in the hierarchy. 因为offsetLeftoffsetTop是元素的“外部”左上角与其偏移父级的“内部”左上角之间的差异,所以总和位置将跳过层次结构中的所有边框和填充。

Here's a comparison that should (hopefully) clarify what I mean. 这是一个比较,应该(希望)澄清我的意思。

  • If I get the sum of the distances in position between the 'outer' top left of the elements and the 'inner' top left of their offset parents ( outers minus inners ; what I am doing right now), I get the element's content area's position, minus all the borders and paddings in the offset hierarchy. 如果我得到元素的“外部”左上角和偏移父母的“内部”左上角之间的距离总和( outers减去内部 ;我现在正在做什么),我得到元素的内容区域position,减去偏移层次结构中的所有边框和填充。
  • If I get the sum of the distances in position between the 'outer' top left of the elements and the 'outer' top left of their offset parents ( outers minus outers ), I get the element's content area's position, minus the border and padding of the desired element (close, but not quite there). 如果我得到元素的“外部”左上角和偏移父母的“外部”左上角之间的距离总和( outers减去outers ),我得到元素的内容区域的位置,减去边框和填充期望的元素(关闭,但不完全)。
  • If I get the sum of the distances in position between the 'inner' top left of the elements and the 'inner' top left of their offset parents ( inners minus inners ), I get the element's content area's position. 如果我得到元素的“内部”左上角和偏移父母的内部“内部”( 内部减去内部 )之间的距离总和,我得到元素的内容区域的位置。 This is what I want. 这就是我要的。

Here's a live example that uses an element_position() function that is aware of padding and borders. 这是一个实例,它使用了一个知道填充和边框的element_position()函数。 I've added some extra padding and margins to your original example. 我在原始示例中添加了一些额外的填充和边距。

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

To use it, move the cursor over the brown area. 要使用它,请将光标移到棕色区域上。 The resulting white area is the actual canvas content. 生成的白色区域是实际的画布内容。 The brown is padding, the red is a border, and so on. 棕色是填充物,红色是边框,依此类推。 In both this example and the one later on, the canvas x and canvas y readouts indicate the cursor position relative to canvas content. 在此示例和稍后的示例中, canvas xcanvas y读数指示相对于画布内容的光标位置。

Here's the code for element_position() : 这是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 };
}

The code should work properly in IE9, FF and Chrome, although I notice it is not quite right in Opera. 代码应该在IE9,FF和Chrome中正常工作,虽然我注意到它在Opera中并不完全正确。

My original inclination was to use something like the e.offsetX/Y properties because they were closer to what you want, and do not involve looping over nested elements. 我最初倾向于使用类似e.offsetX/Y属性的东西,因为它们更接近你想要的东西,并且不涉及循环嵌套元素。 However, their behaviour varies wildly across browsers, so a bit of cross-browser finagling is necessary. 但是,它们在浏览器中的行为差别很大,因此需要进行一些跨浏览器的处理。 The live example is here: 现场示例如下:

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

It should work across all modern browsers - Opera, FF, Chrome, IE9. 它应该适用于所有现代浏览器 - Opera,FF,Chrome,IE9。 I personally prefer it, but thought that although your original question was just about "getting mouse position relative to content area of an element", you were really asking about how to make the element_position() function work correctly. 我个人更喜欢它,但认为虽然你的原始问题只是“相对于元素的内容区域获取鼠标位置”,但你真的在询问如何使element_position()函数正常工作。

using jQuery: 使用jQuery:

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;
};

live example: http://jsfiddle.net/vGKM3/ 实例: http//jsfiddle.net/vGKM3/

The root of this is simple: compute the element's position relative to the document. 这个的根本很简单:计算元素相对于文档的位置。 I then drop the top & left padding & border (margin is included with basic positioning calculations). 然后我放下顶部和左边的填充和边框(边距包含在基本定位计算中)。 The internal jQuery code for doing this is based on getComputedStyle and element.currentStyle . 用于执行此操作的内部jQuery代码基于getComputedStyleelement.currentStyle Unfortunately I don't think there is another way... 不幸的是我不认为还有另一种方式......

The core of jQuery's .offset() function, which gets an element's position relative to document: 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
}

Theoretically, another way to do this would be, using the above positioning code: 从理论上讲,另一种方法是使用上面的定位代码:

  • make sure your element has position: relative (or absolute) set 确保你的元素有position: relative (或绝对)设置
  • append a new element with position: absolute; top:0px; left:0px; position: absolute; top:0px; left:0px;附加一个新元素position: absolute; top:0px; left:0px; position: absolute; top:0px; left:0px;
  • get the position of the new element relative to the document. 获取元素相对于文档的位置。 It will be the same as the content position of the parent 它将与父级的内容位置相同
  • delete the new element 删除新元素

In your element_position(e) function, iterate through the hierarchy using parentNode , get the padding, offsets and border using getComputedStyle(e, null).getPropertyValue(each_css) , and sum them to the value of your x and y values before return. element_position(e)函数中,使用parentNode迭代层次结构,使用getComputedStyle(e, null).getPropertyValue(each_css)获取填充,偏移和边框,并在返回之前将它们与xy值的值相加。

There's a post proposing a cross-browser reading of styles here: 有一篇帖子提出了跨浏览器阅读样式:

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

I am not sure if this is the best way, or most resource efficient... 我不确定这是最好的方式,还是资源效率最高......

But I would suggest getting X/Y for the canvas tag, width of the border, and padding and using them all together as the offset. 但我建议为画布标签获取X / Y,边框宽度和填充,并将它们一起用作偏移量。

Edit: 编辑:

Use offsetLeft and offsetTop 使用offsetLeft和offsetTop

Reference: How to Use the Canvas and Draw Elements in HTML5 参考: 如何在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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM