Làm cách nào để có được vị trí hàng đầu của một phần tử so với chế độ xem của trình duyệt?


173

Tôi muốn có được vị trí của một yếu tố liên quan đến chế độ xem của trình duyệt (chế độ xem trong đó trang được hiển thị, không phải toàn bộ trang). Làm thế nào điều này có thể được thực hiện trong JavaScript?

Cảm ơn nhiều


2
Tôi nghĩ rằng câu hỏi này tương tự như stackoverflow.com/questions/211703/ mà có một giải pháp thực sự tốt.
Jakob Runge

1
@JakobRunge, đó là "tốt đẹp" trong năm 2013?
Iulian Onofrei

1
Xem xét chấp nhận một giải pháp.
Iulian Onofrei

Câu trả lời:


305

Các câu trả lời hiện đã lỗi thời. getBoundingClientRect()Phương thức gốc đã xuất hiện khá lâu và thực hiện chính xác những gì câu hỏi yêu cầu. Thêm vào đó, nó được hỗ trợ trên tất cả các trình duyệt (bao gồm cả IE 5, dường như!)

Từ trang MDN :

Giá trị được trả về là một đối tượng TextRonymous, chứa các thuộc tính chỉ đọc bên trái, trên cùng, bên phải và dưới cùng mô tả hộp viền, tính bằng pixel, với góc trên bên trái so với góc trên bên trái của chế độ xem .

Bạn sử dụng nó như vậy:

var viewportOffset = el.getBoundingClientRect();
// these are relative to the viewport, i.e. the window
var top = viewportOffset.top;
var left = viewportOffset.left;

3
Điều này có thể không hoạt động chính xác khi sử dụng thu phóng trình duyệt (Android Chrome). Giải pháp từ @rism ( xem bên dưới ) hoạt động trong trường hợp này
Dmitry

4
Tốt cho hầu hết các trường hợp, nhưng nó giả sử phần tử không nằm trong iframe được nhúng, phải không? Câu hỏi là hỏi về tương đối cửa sổ trình duyệt. El.getBoundingClientRect () sẽ trả về tương đối cửa sổ iframe chứ không phải cửa sổ trình duyệt.
cakidnyc

6
Câu hỏi là hỏi về tương đối cửa sổ trình duyệt.
Dmitry Dvornikov

2
@Denilson getBoundingClientRect().topkhông có trong IE11 của tôi, nó trả về 0. Bạn có giải pháp nào không.
Arif

@Arif: Xin lỗi, nhưng tôi không thể tái tạo vấn đề của bạn. Tôi đã thử nghiệm điều này trên IE11 và vẫn nhận được kết quả: codepen.io/denilsonsa/pen/ypqyXj
Denilson Sá Maia

57

Trong trường hợp của tôi, để an toàn khi cuộn, tôi đã thêm window.scroll vào phương trình:

var element = document.getElementById('myElement');
var topPos = element.getBoundingClientRect().top + window.scrollY;
var leftPos = element.getBoundingClientRect().left + window.scrollX;

Điều đó cho phép tôi có được vị trí tương đối thực sự của phần tử trên tài liệu, ngay cả khi nó đã được cuộn.


2
Bạn không nên thêm vị trí cuộn thay vì trừ nó?
Iulian Onofrei

Tôi nêu lên câu trả lời nhưng vẫn, @lulian Onofrei đã đúng. Vui lòng chỉnh sửa.
pmrotule

@Fabio getBoundingClientRect().topkhông có trong IE11 của tôi, nó trả về 0. Bạn có giải pháp nào không.
Arif

1
@Arif window.scrollY || window.pageYOffsetcó thể cải thiện hỗ trợ
Fabian von Ellerts

@FabianvonEllerts :)
Arif

15

Chỉnh sửa: Thêm một số mã vào tài khoản để cuộn trang.

function findPos(id) {
    var node = document.getElementById(id);     
    var curtop = 0;
    var curtopscroll = 0;
    if (node.offsetParent) {
        do {
            curtop += node.offsetTop;
            curtopscroll += node.offsetParent ? node.offsetParent.scrollTop : 0;
        } while (node = node.offsetParent);

        alert(curtop - curtopscroll);
    }
}

Đối số id là id của phần tử có phần bù bạn muốn. Chuyển thể từ một bài quirksmode .


1
Cảm ơn câu trả lời của bạn nhưng tôi nghĩ bạn đã hiểu nhầm câu hỏi của tôi, tôi biết cách lấy đầu của một yếu tố liên quan đến trang, điều tôi đang cố gắng làm là có được vị trí liên quan đến 'khung nhìn' (ví dụ: cửa sổ trình duyệt , không phải tài liệu)
Waleed Eissa

Xin lỗi, việc bạn sử dụng 'khung nhìn' đã ném tôi. Vì vậy, bạn đang nói rằng bạn muốn tài khoản cho trình duyệt chrome, tức là thanh công cụ bookmark, thanh vị trí, v.v?
Derek Swingley

Không, chỉ là cửa sổ mà bạn nhìn thấy trang. Nếu trang được cuộn, nó sẽ khác với đầu tài liệu, nếu không thì chúng giống nhau.
Wissa bị bỏ rơi

Ồ, tôi hiểu rồi. Không chắc làm thế nào để làm điều đó ... sẽ chỉnh sửa câu trả lời của tôi nếu tôi nghĩ ra thứ gì đó.
Derek Swingley

Tôi đã tạo mã ở trên xấu hơn một chút nhưng nó hoạt động ... sử dụng offsetParent.scrollTop
Derek Swingley

10
var element =  document.querySelector('selector');
var bodyRect = document.body.getBoundingClientRect(),
    elemRect = element.getBoundingClientRect(),
    offset   = elemRect.top - bodyRect.top;

Điều này dường như tính toán từ đầu tài liệu, không phải chế độ xem
Scott Leonard

6

Bạn co thể thử:

node.offsetTop - window.scrollY

Nó hoạt động trên Opera với thẻ meta viewport được xác định.


7
Nếu tôi hiểu tài liệu chính xác, offsetTopcó liên quan đến yếu tố định vị gần nhất. Mà, tùy thuộc vào cấu trúc trang, có thể hoặc không thể là phần tử gốc.
Denilson Sá Maia

5

jQuery thực hiện điều này khá thanh lịch. Nếu bạn xem nguồn của jQuery offset, bạn sẽ thấy đây là cách nó được triển khai:

var rect = elem.getBoundingClientRect();
var win = elem.ownerDocument.defaultView;

return {
    top: rect.top + win.pageYOffset,
    left: rect.left + win.pageXOffset
};

2

Các chức năng trên này Trang sẽ trở lại một hình chữ nhật với đầu, bên trái, chiều cao và phối hợp chiều rộng của một yếu tố thông qua liên quan đến các cổng xem trình duyệt.

    localToGlobal: function( _el ) {
       var target = _el,
       target_width = target.offsetWidth,
       target_height = target.offsetHeight,
       target_left = target.offsetLeft,
       target_top = target.offsetTop,
       gleft = 0,
       gtop = 0,
       rect = {};

       var moonwalk = function( _parent ) {
        if (!!_parent) {
            gleft += _parent.offsetLeft;
            gtop += _parent.offsetTop;
            moonwalk( _parent.offsetParent );
        } else {
            return rect = {
            top: target.offsetTop + gtop,
            left: target.offsetLeft + gleft,
            bottom: (target.offsetTop + gtop) + target_height,
            right: (target.offsetLeft + gleft) + target_width
            };
        }
    };
        moonwalk( target.offsetParent );
        return rect;
}

2
function inViewport(element) {
    let bounds = element.getBoundingClientRect();
    let viewWidth = document.documentElement.clientWidth;
    let viewHeight = document.documentElement.clientHeight;

    if (bounds['left'] < 0) return false;
    if (bounds['top'] < 0) return false;
    if (bounds['right'] > viewWidth) return false;
    if (bounds['bottom'] > viewHeight) return false;

    return true;
}

nguồn


1

Cảm ơn tất cả các câu trả lời. Có vẻ như Prototype đã có một hàm thực hiện chức năng này (hàm page ()). Bằng cách xem mã nguồn của hàm, tôi thấy rằng trước tiên, nó tính toán vị trí bù phần tử so với trang (tức là phần trên cùng của tài liệu), sau đó trừ scrollTop từ đó. Xem mã nguồn của nguyên mẫu để biết thêm chi tiết.


Quyết định tốt. Có lẽ tốt nhất là đi với một trong những thư viện lớn vì họ có nhiều khả năng đưa ra kết quả tương tự trên các trình duyệt. Nếu bạn tò mò, hãy kiểm tra câu trả lời của tôi. Mã ở đó sẽ làm điều này nhưng tôi không biết làm thế nào nó sẽ giữ trên các trình duyệt.
Derek Swingley

1

Tôi giả sử một phần tử có id btn1tồn tại trong trang web và cũng bao gồm jQuery. Điều này đã hoạt động trên tất cả các trình duyệt hiện đại của Chrome, FireFox, IE> = 9 và Edge. jQuery chỉ được sử dụng để xác định vị trí liên quan đến tài liệu.

var screenRelativeTop =  $("#btn1").offset().top - (window.scrollY || 
                                            window.pageYOffset || document.body.scrollTop);

var screenRelativeLeft =  $("#btn1").offset().left - (window.scrollX ||
                                           window.pageXOffset || document.body.scrollLeft);

1
jQuery là JavaScript. Tôi tin rằng bạn có nghĩa là "nó không phải là 100% vani / thuần túy."
hungerstar

Vâng, đó là những gì tôi muốn nói.
Sunil

1

Đôi khi getBoundingClientRect()giá trị thuộc tính của đối tượng hiển thị 0 cho IE. Trong trường hợp đó bạn phải đặtdisplay = 'block' cho phần tử. Bạn có thể sử dụng mã dưới đây cho tất cả các trình duyệt để được bù đắp.

Mở rộng chức năng jQuery:

(function($) {
    jQuery.fn.weOffset = function () {
        var de = document.documentElement;
        $(this).css("display", "block");
        var box = $(this).get(0).getBoundingClientRect();
        var top = box.top + window.pageYOffset - de.clientTop;
        var left = box.left + window.pageXOffset - de.clientLeft;
        return { top: top, left: left };
    };
}(jQuery));

Sử dụng :

var elementOffset = $("#" + elementId).weOffset();
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.