Nhận vị trí / độ lệch của phần tử liên quan đến vùng chứa mẹ?


125

Tôi đã quen làm việc với jQuery. Tuy nhiên, trong dự án hiện tại của tôi, tôi sử dụng zepto.js. Zepto không cung cấp một position()phương thức như jQuery. Zepto chỉ đi kèm với offset().

Bất kỳ ý tưởng nào về cách tôi có thể truy xuất phần bù của vùng chứa liên quan đến nguồn gốc với js thuần túy hoặc với Zepto?


4
Bỏ phiếu từ chối chấp nhận câu trả lời jQuery (thậm chí không được gắn nhãn là jQuery!) Khi đặt câu hỏi JavaScript.
John

@John bạn có nhận thấy thẻ zepto không?
Supersharp

@Supersharp Sử dụng JavaScriptthẻ khi bạn đang sử dụng JavaScript thuần túy. Nếu bạn đang sử dụng khuôn khổ hoặc thư viện thì bạn không sử dụng JavaScriptthẻ. Nếu tên của bạn là Jeff, tôi sẽ không gọi bạn là Sally.
John,

@John đó là một ý kiến ​​nhưng nó không phải là cách SO hoạt động. Vui lòng đọc hướng dẫn thẻ javascript. Trong trường hợp này, nó nên được sử dụng cùng với thẻ thư viện.
Supersharp

@Supersharp Yeah SO bất tài như thế; lạm phát thẻ theo nghĩa đen. 😒︎
John,

Câu trả lời:


188

Cảnh báo: jQuery, không phải JavaScript chuẩn

element.offsetLeftelement.offsetToplà các thuộc tính javascript thuần túy để tìm vị trí của một phần tử liên quan đến nó offsetParent; là phần tử mẹ gần nhất với vị trí của relativehoặcabsolute

Ngoài ra, bạn luôn có thể sử dụng Zepto để lấy vị trí của một phần tử VÀ phần tử gốc của nó và chỉ cần trừ hai phần tử:

var childPos = obj.offset();
var parentPos = obj.parent().offset();
var childOffset = {
    top: childPos.top - parentPos.top,
    left: childPos.left - parentPos.left
}

Điều này có lợi là mang lại cho bạn sự bù đắp của một đứa trẻ so với cha mẹ của nó ngay cả khi cha mẹ không được định vị.


9
Vị trí của phần tử statickhông phải là cha mẹ bù đắp.
Esailija

5
không sử dụng dấu chấm phẩy bên trong dấu ngoặc, dấu phẩy sử dụng
Luca Borrione

1
những gì về vị trí cố định? họ có bù đắp cho cha mẹ không?
Ayyash

1
Có, @ Ayya- vị trí cố định cũng tạo ra một bối cảnh bù đắp mới
mmmeff

9
@vsync jQuery không phải là vua của bất cứ thứ gì kể từ khi ie9 đạt EOL vào tháng 1 năm 2016, chúng tôi có lựa chọn DOM chuẩn hóa trong tất cả các trình duyệt chính, hãy tìm hiểu js thuần túy. Ngoài ra, các ý kiến ​​về việc sử dụng khuôn khổ nào cũng không có chỗ trong SO, vì nó phụ thuộc rất nhiều vào dự án và nền tảng mục tiêu.
Hans Koch

72

trong js thuần túy chỉ sử dụng offsetLeftoffsetTopthuộc tính.
Ví dụ về fiddle: http://jsfiddle.net/WKZ8P/

var elm = document.querySelector('span');
console.log(elm.offsetLeft, elm.offsetTop);
p   { position:relative; left:10px; top:85px; border:1px solid blue; }
span{ position:relative; left:30px; top:35px; border:1px solid red; }
<p>
    <span>paragraph</span>
</p>


Cảm ơn bạn. Cũng có cách nào để lấy giá trị bù cho phần tử cha.parent?
matt,

2
p.offsetTop + p.parentNode.offsetTopBạn có thể gói nó trong một lệnh gọi hàm đệ quy giống như PPK đã đề xuất một vài năm trước. Bạn có thể thay đổi chức năng để vượt qua số cấp độ để tăng lên hoặc sử dụng một bộ chọn để bắt chước $(selector).parents(parentSelector). Tôi thích giải pháp này hơn vì việc triển khai zepto chỉ hoạt động trên các trình duyệt hỗ trợ getBoundingClientsRect- điều mà một số điện thoại di động không có.
Torsten Walter

Tôi nghĩ rằng getBoundingClientsRectđã được ủng hộ rộng rãi ... nếu ai biết điện thoại di động nào không ủng hộ nó, tôi sẽ quan tâm (không thể tìm thấy bất kỳ bảng hỗ trợ nào cho điện thoại di động về nó).
Nobita,

Đây có lẽ là câu trả lời đúng nhất mặc dù nó không được đánh dấu. Giải pháp cá nhân của tôi đã được thay đổi $(this).offset().leftthành this.offsetLeft.
adjwilli

Điều này cũng khắc phục jQuery.position()hành vi sai bên trong của cha mẹ được chuyển đổi 3d, cảm ơn bạn rất nhiều!
rassoh

6

Tôi đã làm như thế này trong Internet Explorer.

function getWindowRelativeOffset(parentWindow, elem) {
    var offset = {
        left : 0,
        top : 0
    };
    // relative to the target field's document
    offset.left = elem.getBoundingClientRect().left;
    offset.top = elem.getBoundingClientRect().top;
    // now we will calculate according to the current document, this current
    // document might be same as the document of target field or it may be
    // parent of the document of the target field
    var childWindow = elem.document.frames.window;
    while (childWindow != parentWindow) {
        offset.left = offset.left + childWindow.frameElement.getBoundingClientRect().left;
        offset.top = offset.top + childWindow.frameElement.getBoundingClientRect().top;
        childWindow = childWindow.parent;
    }

    return offset;
};

=================== bạn có thể gọi nó như thế này

getWindowRelativeOffset(top, inputElement);

Tôi chỉ tập trung vào IE theo trọng tâm của tôi nhưng những thứ tương tự có thể được thực hiện cho các trình duyệt khác.


1

Thêm phần bù của sự kiện vào phần tử gốc để có được vị trí bù tuyệt đối của sự kiện.

Một ví dụ :

HTMLElement.addEventListener('mousedown',function(e){

    var offsetX = e.offsetX;
    var offsetY = e.offsetY;

    if( e.target != this ){ // 'this' is our HTMLElement

        offsetX = e.target.offsetLeft + e.offsetX;
        offsetY = e.target.offsetTop + e.offsetY;

    }
}

Khi mục tiêu sự kiện không phải là phần tử mà sự kiện đã được đăng ký, nó sẽ thêm phần bù của phần tử gốc vào phần bù của sự kiện hiện tại để tính toán giá trị bù "Tuyệt đối".

Theo Mozilla Web API: " Thuộc tính chỉ đọc HTMLElement.offsetLeft trả về số lượng pixel mà góc trên bên trái của phần tử hiện tại được bù sang bên trái trong nút HTMLElement.offsetParent. "

Điều này chủ yếu xảy ra khi bạn đăng ký một sự kiện trên cha mẹ chứa nhiều con hơn, ví dụ: một nút có biểu tượng bên trong hoặc khoảng văn bản, một liphần tử có các khoảng bên trong. Vân vân...


Còn con của bố mẹ thì sao? Điều gì về các phần tử lồng nhau có thanh cuộn? Điều gì về các phần tử trước đó với hiển thị: không có? Vị trí bù đắp của tài liệu thực sự gần như không thể tính được, nhưng công cụ kết xuất có thể biết được điều đó. Các thuộc tính đơn giản quá tệ như vị trí tài liệu không bao giờ được đưa vào DOM.
David Spector

@DavidS Inspector word

1

Thí dụ

Vì vậy, nếu chúng tôi có một phần tử con với id là "phần tử con" và chúng tôi muốn đặt nó ở vị trí bên trái / trên cùng so với phần tử mẹ, giả sử một div có lớp là "item-parent", chúng tôi sẽ sử dụng mã này.

var position = $("#child-element").offsetRelative("div.item-parent");
alert('left: '+position.left+', top: '+position.top);

Plugin Cuối cùng, đối với plugin thực tế (với một số ghi chú giải thích những gì đang xảy ra):

// offsetRelative (or, if you prefer, positionRelative)
(function($){
    $.fn.offsetRelative = function(top){
        var $this = $(this);
        var $parent = $this.offsetParent();
        var offset = $this.position();
        if(!top) return offset; // Didn't pass a 'top' element 
        else if($parent.get(0).tagName == "BODY") return offset; // Reached top of document
        else if($(top,$parent).length) return offset; // Parent element contains the 'top' element we want the offset to be relative to 
        else if($parent[0] == $(top)[0]) return offset; // Reached the 'top' element we want the offset to be relative to 
        else { // Get parent's relative offset
            var parent_offset = $parent.offsetRelative(top);
            offset.top += parent_offset.top;
            offset.left += parent_offset.left;
            return offset;
        }
    };
    $.fn.positionRelative = function(top){
        return $(this).offsetRelative(top);
    };
}(jQuery));

Lưu ý: Bạn có thể sử dụng tính năng này trên sự kiện mouseClick hoặc mouseover

$(this).offsetRelative("div.item-parent");

Tôi không thấy tính năng cuộn. Có rất nhiều trường hợp kỳ quặc có thể xảy ra.
David Spector

1

Tôi có một giải pháp khác. Trừ giá trị thuộc tính mẹ khỏi giá trị thuộc tính con

$('child-div').offset().top - $('parent-div').offset().top;

0

Chắc chắn là dễ dàng với JS thuần túy, chỉ cần làm điều này, hoạt động cho các bảng HTML 5 cố định và hoạt hình, tôi đã tạo và thử mã này và nó hoạt động cho bất kỳ trình duyệt nào (bao gồm IE 8):

<script type="text/javascript">
    function fGetCSSProperty(s, e) {
        try { return s.currentStyle ? s.currentStyle[e] : window.getComputedStyle(s)[e]; }
        catch (x) { return null; } 
    }
    function fGetOffSetParent(s) {
        var a = s.offsetParent || document.body;

        while (a && a.tagName && a != document.body && fGetCSSProperty(a, 'position') == 'static')
            a = a.offsetParent;
        return a;
    }
    function GetPosition(s) {
        var b = fGetOffSetParent(s);

        return { Left: (b.offsetLeft + s.offsetLeft), Top: (b.offsetTop + s.offsetTop) };
    }    
</script>

Bạn đã thử nghiệm với tất cả các trường hợp có thể có của các nút cha và thuộc tính phần tử chưa? Điều này có tài khoản cho cuộn và cuộn lồng nhau không?
David Spector
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.