Truy xuất vị trí (X, Y) của phần tử HTML so với cửa sổ trình duyệt


1570

Tôi muốn biết làm thế nào để có được vị trí X và Y của các phần tử HTML như imgdivtrong JavaScript so với cửa sổ trình duyệt.


134
Liên quan đến cái gì?
AnthonyWJones

4
tôi đã sử dụng 7 dòng mã này hoạt động trong tất cả các trình duyệt có sự khác biệt trong tức là 5,6,7 (tôi không nhớ có bất kỳ proboem nào ... có thể là loại tài liệu) ... quirksmode.org/js/findpose. html và tôi đã sử dụng nó rất nhiều trong nhiều năm. có thể sombody có thể chỉ ra sai sót nếu có.
Jayapal Chandran

98
@AnthonyWJones, tôi cho rằng bạn cần niềm vui nhi đồng, nhưng rõ ràng, vì luôn luôn là khi không xác định, OP đề cập đến trường hợp chung nhất hoặc đề cập đến tọa độ cửa sổ của trình duyệt.
Bỏ phiếu

7
@Mote, Không, nó không rõ ràng. Để lại suy luận, chủ quan và tiên đề sai sang một bên. Nó có thể liên quan đến viewport hoặc đầu trang (còn gọi là document.documentEuity).
Andre Figueiredo

15
Mặc định chung chung nhất không phải là suy luận, đó là sự tiến triển hợp lý, do đó sẽ liên quan đến cửa sổ, hoặc "đầu trang" có thể là thuật ngữ như bạn đặt. Trong Lý thuyết trò chơi, nó được mã hóa thành một khái niệm gọi là điểm Schelling. Hãy chắc chắn để xác định khi bạn không có nghĩa là trường hợp chung nhất.
Bỏ phiếu

Câu trả lời:


1796

Cách tiếp cận đúng là sử dụng element.getBoundingClientRect():

var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);

Internet Explorer đã hỗ trợ điều này miễn là bạn có thể quan tâm và cuối cùng nó đã được chuẩn hóa trong Chế độ xem CSSOM . Tất cả các trình duyệt khác đã thông qua nó một thời gian dài trước đây .

Một số trình duyệt cũng trả về thuộc tính chiều cao và chiều rộng, mặc dù điều này là không chuẩn. Nếu bạn lo lắng về khả năng tương thích trình duyệt cũ hơn, hãy kiểm tra bản sửa đổi của câu trả lời này để biết cách triển khai xuống cấp được tối ưu hóa.

Các giá trị được trả về element.getBoundingClientRect()là tương đối với khung nhìn. Nếu bạn cần nó so với phần tử khác, chỉ cần trừ một hình chữ nhật từ phần tử khác:

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

alert('Element is ' + offset + ' vertical pixels from <body>');

143
Một bài viết rất thú vị về tọa độ JS . Bài viết này không được đề cập ở đâu trên SO, nó nên ..
e2-e4

6
Không hoạt động ... đó là 8 pixel theo chiều dọc và chiều ngang, do lề 8px trong thân máy. Giải pháp của Meouw hoạt động hoàn hảo. Nếu bạn muốn tôi có một bài kiểm tra nhỏ để chứng minh vấn đề.
CpnCrunch

3
Trên thực tế, tôi nghĩ rằng nó phụ thuộc vào những gì bạn thực sự muốn có được các hợp đồng. Câu hỏi hơi mơ hồ. Câu trả lời của bạn là chính xác nếu bạn chỉ muốn các hợp đồng liên quan đến khung nhìn hoặc liên quan đến yếu tố cơ thể, nhưng điều đó không giúp ích gì cho bạn trong trường hợp bạn muốn vị trí tuyệt đối của yếu tố (mà tôi tưởng tượng là điều mà hầu hết mọi người muốn) . Câu trả lời của meouw cũng không cung cấp cho bạn điều đó, trừ khi bạn loại bỏ các bit '-scrollLeft' và '-scrollTop'.
CpnCrunch

4
@CpnCrunch: Tôi hiểu ý của bạn lúc này; Tôi không nhận ra bạn đang đề cập đến phần sau của câu trả lời của tôi. Khi thân máy có lề, hộp giới hạn của nó sẽ được bù bởi số pixel đó ở mỗi bên tương ứng. Trong trường hợp đó, bạn cũng phải trừ kiểu lề được tính từ tọa độ cơ thể. <body>Mặc dù vậy, đáng lưu ý rằng CSS đặt lại loại bỏ lề từ .
Andy E

3
element.getBoundingClientRect().topkhông trả lại phần bù trên cùng chính xác trong trường hợp position: fixedphần tử trong iOS (iPhone 8) nếu phần tử này chứa phần tử nhập tập trung và bàn phím ảo bị trượt lên. Ví dụ: nếu phần bù thực tế từ đỉnh của cửa sổ là 200px, thì nó sẽ báo cáo là 420px, trong đó 220px là chiều cao của bàn phím ảo. Nếu bạn đặt phần tử position: absolutevà đặt nó ở cùng một vị trí như cố định, thì bạn sẽ nhận được 200px chính xác. Tôi không biết một giải pháp cho vấn đề này.
Jāni Elmeris

327

Các thư viện đi đến một số độ dài để có được độ lệch chính xác cho một phần tử.
Đây là một chức năng đơn giản thực hiện công việc trong mọi trường hợp mà tôi đã thử.

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}
var x = getOffset( document.getElementById('yourElId') ).left; 

14
thay đổi: el = el.parentNode thành: el = el.offsetParent; và có vẻ như nó hoạt động cho các iframe lồng nhau ... Tôi đang nghĩ đó là những gì bạn dự định?
Adam

4
Giải pháp này không đầy đủ. Hãy thử đặt một đường viền dày trên một div và làm tổ nó một vài lần. Trong bất kỳ phiên bản Firefox nào (2-5), nó sẽ bị tắt bởi (các) độ rộng đường viền (ngay cả trong chế độ tuân thủ tiêu chuẩn).
ck_

3
không có cách nào sạch hơn để làm điều đó, như hàm el.total OffersetLeft và Total OffersetTop? jQuery chắc chắn có tùy chọn này nhưng thật xấu hổ vì không có phương thức chính thức nào cho việc này, chúng ta có cần đợi DOM4 không? Tôi đang xây dựng một ứng dụng canvas HTML5, vì vậy tôi nghĩ giải pháp tốt nhất là chèn phần tử canvas vào iframe để tôi có thể có tọa độ thực với clientX và clientY khi chúng tôi nhấp vào phần tử.
baptx

1
Vì vậy, @Adam và meouw, bạn đang nói khối mã đầu tiên là sai? Vậy thì tại sao không loại bỏ hoàn toàn? (Câu hỏi này đã thấy khá nhiều người xem trong những năm qua; có thể tốt để xóa mọi thứ nếu chúng sai?)
Arjan

2
@baptx: Tôi biết đây là phản hồi muộn, nhưng tôi không thấy bình luận của bạn sớm hơn. Xem câu trả lời của tôi dưới đây để biết cách thay thế tuân thủ tiêu chuẩn hơn - bạn thậm chí không cần mã dự phòng, vì các trình duyệt đã hỗ trợ nó trong một thời gian dài.
Andy E

242

Điều này làm việc cho tôi (sửa đổi từ câu trả lời được bình chọn cao nhất):

function getOffset(el) {
  const rect = el.getBoundingClientRect();
  return {
    left: rect.left + window.scrollX,
    top: rect.top + window.scrollY
  };
}

Sử dụng cái này chúng ta có thể gọi

getOffset(element).left

hoặc là

getOffset(element).top

1
Chỉ giải pháp này hoạt động với tôi khi phần tử được đặt ở divgiữa mà sử dụng css top:50%, left:50%; transform:translate(-50%, -50%);... xuất sắc. Đồng ý câu hỏi @ScottBiggs whit. Logic là để tìm kiếm sự đơn giản.
nelek

3
có thể nó không phải là người chiến thắng vì nó giống như giải pháp của Andy E, nhưng không hoạt động trong các trình duyệt cũ <IE9
niltoid

getBoundingClientRect gây ra sự tăng đột biến lớn trong bảng xếp hạng hiệu suất của tôi
frumbert

1
Bạn nên xác định rectsử dụng var, lethoặc const. Mặt khác, nó được định nghĩa là window.rect.
hev1

2
left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2)sẽ trả lại vị trí chính giữa của một yếu tố
hủy bỏ

97

Nếu bạn muốn nó chỉ được thực hiện trong javascript , đây là một số liners sử dụnggetBoundingClientRect()

window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y

window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X

Dòng đầu tiên sẽ trả về offsetTopY liên quan đến tài liệu. Dòng thứ hai sẽ trả về offsetLeftX liên quan đến tài liệu.

getBoundingClientRect() là một hàm javascript trả về vị trí của phần tử so với khung nhìn của cửa sổ.


2
Đây nên là giải pháp. Không có bs mã dài ở đây, nó dễ hiểu và rất chính xác!
E Alexis MT

2
Nếu phần tử nằm trong phần tử có thể cuộn thì sao? Cách duy nhất để làm điều đó là tính tổng offsetTop của tất cả các phần tử cha, như các câu trả lời khác gợi ý
Dmitri Pisarev

Như Dmitri tuyên bố, điều này là không chính xác và rất thiếu sót. Nó không nên có nhiều phiếu bầu như nó làm. Nó cũng sẽ luôn ở trong một yếu tố có thể cuộn được vì bản thân tài liệu có thể được cuộn. Nói cách khác, trừ khi toàn bộ tài liệu phù hợp với chế độ xem, nó sẽ luôn không chính xác nếu người dùng đã cuộn cửa sổ chính.
Lev

46

Các phần tử HTML trên hầu hết các trình duyệt sẽ có: -

offsetLeft
offsetTop

Những đặc điểm này xác định vị trí của phần tử tương đối cha mẹ gần nhất có bố cục. Cha mẹ này thường có thể được truy cập bif thuộc tính offsetParent.

IE và FF3 có

clientLeft
clientTop

Các thuộc tính này ít phổ biến hơn, chúng chỉ định vị trí các thành phần với vùng máy khách cha mẹ của nó (vùng đệm là một phần của vùng máy khách nhưng đường viền và lề không).


36

Nếu trang bao gồm - ít nhất là - bất kỳ "DIV" nào, chức năng do meouw cung cấp sẽ ném giá trị "Y" vượt quá giới hạn trang hiện tại. Để tìm vị trí chính xác, bạn cần xử lý cả offsetParent và ParentNode.

Hãy thử mã được đưa ra dưới đây (nó được kiểm tra cho FF2):


var getAbsPosition = function(el){
    var el2 = el;
    var curtop = 0;
    var curleft = 0;
    if (document.getElementById || document.all) {
        do  {
            curleft += el.offsetLeft-el.scrollLeft;
            curtop += el.offsetTop-el.scrollTop;
            el = el.offsetParent;
            el2 = el2.parentNode;
            while (el2 != el) {
                curleft -= el2.scrollLeft;
                curtop -= el2.scrollTop;
                el2 = el2.parentNode;
            }
        } while (el.offsetParent);

    } else if (document.layers) {
        curtop += el.y;
        curleft += el.x;
    }
    return [curtop, curleft];
};

1
như với các giải pháp khác ngay cả với FF 24.4, đoạn mã trên không hoạt động khi độ rộng đường viền tồn tại như một phần của bố cục định vị.
cướp

Bạn là một người cứu rỗi cuộc sống. Tôi hiện đang xử lý một số lỗi GWT khá sâu và ném nó vào phương thức JSNI giải quyết tất cả các vấn đề của tôi !!
Will Byrne

35

Bạn có thể thêm hai thuộc tính để Element.prototypecó được đỉnh / trái của bất kỳ phần tử nào.

Object.defineProperty( Element.prototype, 'documentOffsetTop', {
    get: function () { 
        return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
    }
} );

Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
    get: function () { 
        return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
    }
} );

Điều này được gọi như thế này:

var x = document.getElementById( 'myDiv' ).documentOffsetLeft;

Đây là bản demo so sánh kết quả với jQuery offset().top.left: http://jsfiddle.net/ThinkingStiff/3G7EZ/


14
Sửa đổi Element.prototypethường được coi là một ý tưởng tồi . Nó dẫn đến thực sự khó khăn để duy trì mã. Ngoài ra, mã này không có tài khoản để cuộn.
Jared Forsyth

23

Để truy xuất vị trí liên quan đến trang một cách hiệu quả và không sử dụng chức năng đệ quy: (bao gồm cả IE)

var element = document.getElementById('elementId'); //replace elementId with your element's Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
                document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?                   
                 document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;

Bao gồm tất cả scrollTop / scrollLeft quan trọng làm cho câu trả lời này đúng hơn một chút.
scunliffe

19

Làm thế nào về một cái gì đó như thế này, bằng cách chuyển ID của phần tử và nó sẽ trả về bên trái hoặc trên cùng, chúng ta cũng có thể kết hợp chúng:

1) tìm trái

function findLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.left + window.scrollX;
} //call it like findLeft('#header');

2) tìm đầu trang

function findTop(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.top + window.scrollY;
} //call it like findTop('#header');

hoặc 3) tìm bên trái và trên cùng

function findTopLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return {top: rec.top + window.scrollY, left: rec.left + window.scrollX};
} //call it like findTopLeft('#header');

16

Bạn có thể được phục vụ tốt hơn bằng cách sử dụng khung JavaScript, có chức năng trả về thông tin đó (và hơn thế nữa!) Theo cách độc lập với trình duyệt. Ở đây có một ít:

Với các khung này, bạn có thể làm một cái gì đó như: $('id-of-img').top để có được tọa độ y-pixel của hình ảnh.


2
@Costa Tất cả đều sử dụng các loại chức năng đó, mặc dù đây là một trường đang phát triển, vì các trình duyệt khác nhau phù hợp với đặc điểm kỹ thuật khác nhau. Vì vậy, rất nhiều mã liên quan đến việc "sửa chữa" những thứ sai. Bạn thực sự nên nhìn vào mã nguồn.
Shalom Craimer

18
@scraimer: jQuery và YUI KHÔNG phải là các khung !!! Đây là những thư viện ! Nó không giống nhau. Trích dẫn jquery.com : "jQuery là một thư viện JavaScript nhanh, nhỏ và giàu tính năng ." Trích dẫn yuil Library.com (bạn cũng có thể thấy từ "ma thuật" trong URL ...): "YUI là một thư viện JavaScript và CSS mã nguồn mở, miễn phí để xây dựng các ứng dụng web tương tác phong phú."
Sk8erPeter

29
Vì vậy, bạn nên sử dụng các thư viện lớn chỉ cho nhiệm vụ đơn giản này? Không cần thiết. Tôi ghét rằng mỗi khi tôi muốn làm gì đó với js, tôi sẽ nhận được các giải pháp jQuery này. jQuery không phải là JS!
Opptatt Jobber

1
@OpptattJobber Tôi đồng ý ... JQuery không phải là javascript. Nó dựa vào Javascript nhưng hoàn toàn là ngôn ngữ lập trình khác.
Nick Manning

5
@NickManning Đây không phải là ngôn ngữ mới, đây là lớp trừu tượng cho DOM giúp loại bỏ việc phải viết các giải pháp tương thích và hiệu suất trực tiếp vào mã của bạn. Nếu bạn không sử dụng jQuery, bạn nên sử dụng một số loại lớp trừu tượng.
ginman

15

jQuery .offset () sẽ lấy tọa độ hiện tại của phần tử đầu tiên hoặc đặt tọa độ của mọi phần tử, trong tập hợp các phần tử phù hợp, liên quan đến tài liệu.


Vì một số lý do, nó không cho kết quả tương tự trong IE so với các trình duyệt khác. Tôi nghĩ rằng trong IE nó đang đưa ra vị trí liên quan đến cửa sổ, vì vậy nếu bạn cuộn, bạn sẽ nhận được kết quả khác nhau trong IE so với những người khác
Abdul Rahim Haddad

1
offset () bị nhầm lẫn bởi thu phóng, ít nhất là trong Android Chrome, vì vậy nó vô dụng. stackoverflow.com/a/11396681/1691517 hiển thị cách chịu phóng to.
Timo Kähkönen

10

Tôi đã lấy câu trả lời của @ meouw, được thêm vào clientLeft cho phép tạo đường viền và sau đó tạo ba phiên bản:

getAbsolute OffersetFromBody - tương tự như @ meouw, điều này có được vị trí tuyệt đối so với phần tử hoặc phần tử html của tài liệu (tùy thuộc vào chế độ quirks)

getAbsolute OffersetFromGivenEuity - trả về vị trí tuyệt đối liên quan đến phần tử đã cho (RelEl). Lưu ý rằng phần tử đã cho phải chứa phần tử el hoặc phần tử này sẽ hoạt động giống như getAbsolute OffersetFromBody. Điều này rất hữu ích nếu bạn có hai phần tử được chứa trong một phần tử khác (đã biết) (tùy chọn một số nút trên cây nút) và muốn đặt chúng ở cùng một vị trí.

getAbsolute OffersetFromRelative - trả về vị trí tuyệt đối so với phần tử cha đầu tiên có vị trí: tương đối. Điều này tương tự với getAbsolute OffersetFromGivenEuity, vì lý do tương tự nhưng sẽ chỉ đi xa như là yếu tố phù hợp đầu tiên.

getAbsoluteOffsetFromBody = function( el )
{   // finds the offset of el from the body or html element
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromGivenElement = function( el, relativeEl )
{   // finds the offset of el from relativeEl
    var _x = 0;
    var _y = 0;
    while( el && el != relativeEl && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromRelative = function( el )
{   // finds the offset of el from the first parent with position: relative
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
        if (el != null)
        {
            if (getComputedStyle !== 'undefined')
                valString = getComputedStyle(el, null).getPropertyValue('position');
            else
                valString = el.currentStyle['position'];
            if (valString === "relative")
                el = null;
        }
    }
    return { top: _y, left: _x };
}

Nếu bạn vẫn gặp sự cố, đặc biệt liên quan đến việc cuộn, bạn có thể thử xem tại http://www.greywyvern.com/?post=331 - Tôi nhận thấy ít nhất một đoạn mã nghi vấn trong getStyle sẽ ổn khi giả sử trình duyệt hoạt động tốt , nhưng chưa thử nghiệm phần còn lại.


Thú vị ... nhưng trên Edge getAbsolute OffersetFromBody (phần tử) .top trả về một giá trị khác khi tôi cuộn, trong Chrome nó vẫn nhất quán khi tôi cuộn. Có vẻ như Edge đang điều chỉnh với vị trí cuộn. Khi phần tử được cuộn ra khỏi tầm nhìn, tôi nhận được một giá trị âm.
Norman

getAbsolute OffersetFromRelative đã tạo nên một ngày của tôi, tôi đã phải sao chép tooltip bootstrap mà không cần Javascript của bootstrap, nó đã giúp tôi rất nhiều.
Nolyurn

Chỉ trong trường hợp nếu Meouw thay đổi tên người dùng của họ, hãy liên kết để trả lời: stackoverflow.com/a/442474/3654837 . (Tôi không muốn làm
hỏng

8

nếu sử dụng jQuery, plugin kích thước là tuyệt vời và cho phép bạn chỉ định chính xác những gì bạn muốn.

ví dụ

Vị trí tương đối, vị trí tuyệt đối, vị trí tuyệt đối không có đệm, có đệm ...

Nó tiếp tục, hãy nói rằng có rất nhiều thứ bạn có thể làm với nó.

Ngoài ra, phần thưởng của việc sử dụng jQuery là kích thước tệp gọn nhẹ và dễ sử dụng, bạn sẽ không quay lại JavaScript mà không có nó sau đó.


8

Đây là mã tốt nhất mà tôi đã quản lý để tạo (cũng hoạt động trong iframe, không giống như offset ()) của jQuery. Có vẻ như webkit có một chút hành vi khác nhau.

Dựa trên nhận xét của meouw:

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        // chrome/safari
        if ($.browser.webkit) {
            el = el.parentNode;
        } else {
            // firefox/IE
            el = el.offsetParent;
        }
    }
    return { top: _y, left: _x };
}

Lưu ý rằng bạn sẽ cần jQuerysử dụng $.browser.webkit; Bạn sẽ cần phải chơi xung quanh navigator.userAgentđể làm điều tương tự với thuần túy JavaScript.
SP

2
$ .browser không còn có sẵn trong phiên bản mới nhất của jQuery
Gus

Thật không may, ngay cả với FF 24.4, đoạn mã trên không hoạt động khi độ rộng đường viền tồn tại như một phần của bố cục định vị.
cướp

8

Nếu bạn đang sử dụng jQuery, đây có thể là một giải pháp đơn giản:

<script>
  var el = $("#element");
  var position = el.position();
  console.log( "left: " + position.left + ", top: " + position.top );
</script>

8

Sự khác biệt giữa nhỏ và nhỏ

function getPosition( el ) {
    var x = 0;
    var y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
    x += el.offsetLeft - el.scrollLeft;
    y += el.offsetTop - el.scrollTop;
    el = el.offsetParent;
    }
    return { top: y, left: x };
}

Xem tọa độ ví dụ: http://javascript.info/tutorial/coordins


Thật không may, điều này không hoạt động đối với tất cả các yếu tố mà người ta có thể tìm thấy trong một tài liệu HTML hiện đại. Nhúng <svg>các yếu tố, ví dụ, không có offsetLeft, offsetTopoffsetParenttài sản.
Jonathan Eunice

Nó chỉ DIVhoặc IMG, không SVG. Xem "Nhìn tọa độ ví dụ"? bạn có thể tìm thấy đối tượng svg là vị trí gọi get OffersetRect , thử và phụ thuộc vào công việc của đối tượng.
KingRider

7

Cách tiếp cận sạch nhất mà tôi đã tìm thấy là một phiên bản đơn giản hóa của kỹ thuật được sử dụng bởi jQuery offset. Tương tự như một số câu trả lời khác, nó bắt đầu bằng getBoundingClientRect; sau đó nó sử dụng windowdocumentElementđể điều chỉnh vị trí cuộn cũng như những thứ như lề trên body(thường là mặc định).

var rect = el.getBoundingClientRect();
var docEl = document.documentElement;

var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;


6

Trong khi điều này rất có thể bị mất ở dưới cùng của rất nhiều câu trả lời, các giải pháp hàng đầu ở đây không làm việc cho tôi.
Theo như tôi có thể nói thì không có câu trả lời nào khác có ích.

Tình huống :
Trong trang HTML5 tôi có một menu là phần tử điều hướng bên trong tiêu đề (không phải tiêu đề mà là tiêu đề trong phần tử khác).
Tôi muốn điều hướng bám sát trên cùng một khi người dùng cuộn đến nó, nhưng trước đó tiêu đề này được định vị tuyệt đối (vì vậy tôi có thể để nó phủ lên một cái gì đó khác một chút).
Các giải pháp ở trên không bao giờ kích hoạt thay đổi vì .offsetTop sẽ không thay đổi vì đây là một yếu tố được định vị tuyệt đối. Ngoài ra, thuộc tính .scrollTop đơn giản là đỉnh của phần tử hàng đầu nhất ... nghĩa là 0 và luôn luôn là 0.
Bất kỳ thử nghiệm nào tôi thực hiện sử dụng hai kết quả này (và giống với kết quả getBoundingClientRect) sẽ không cho tôi biết liệu đầu thanh điều hướng có bao giờ cuộn lên đầu trang có thể xem được không (một lần nữa, như đã báo cáo trong bảng điều khiển, chúng chỉ đơn giản giữ nguyên các số trong khi cuộn xảy ra).

Giải pháp Giải
pháp cho tôi là sử dụng

window.visualViewport.pageTop

Giá trị của thuộc tính pageTop phản ánh phần có thể xem của màn hình, do đó cho phép tôi theo dõi vị trí của một phần tử có liên quan đến ranh giới của khu vực có thể xem được.

Có lẽ không cần thiết để nói, bất cứ khi nào tôi đang xử lý cuộn, tôi mong đợi sử dụng giải pháp này để phản ứng theo chương trình đối với chuyển động của các yếu tố được cuộn.
Hy vọng nó sẽ giúp người khác.
LƯU Ý QUAN TRỌNG: Điều này dường như hoạt động trong Chrome và Opera hiện tại & chắc chắn không có trong Firefox (6-2018) ... cho đến khi Firefox hỗ trợ visualViewport tôi khuyên bạn KHÔNG nên sử dụng phương pháp này, và tôi hy vọng họ sẽ sớm làm được ... ý nghĩa hơn phần còn lại).


CẬP NHẬT:
Chỉ là một lưu ý liên quan đến giải pháp này.
Trong khi tôi vẫn thấy những gì tôi phát hiện ra là rất có giá trị cho các tình huống trong đó "... lập trình phản ứng với chuyển động của các yếu tố được cuộn." áp dụng được. Giải pháp tốt hơn cho vấn đề mà tôi gặp phải là sử dụng CSS để thiết lập vị trí: dínhtrên phần tử. Sử dụng dính bạn có thể có một yếu tố luôn ở đầu mà không cần sử dụng javascript (LƯU Ý: đôi khi điều này sẽ không hiệu quả như thay đổi thành phần đó thành cố định nhưng đối với hầu hết các cách sử dụng, phương pháp dính có thể sẽ vượt trội hơn)

CẬP NHẬT01:
Vì vậy, tôi nhận ra rằng đối với một trang khác, tôi có một yêu cầu cần thiết để phát hiện vị trí của một phần tử trong thiết lập cuộn phức tạp nhẹ (thị sai cộng với các phần tử cuộn qua như một phần của thông báo). Tôi nhận ra trong kịch bản đó rằng những điều sau đây đã cung cấp giá trị mà tôi sử dụng để xác định khi nào nên làm gì đó:

  let bodyElement = document.getElementsByTagName('body')[0];
  let elementToTrack = bodyElement.querySelector('.trackme');
  trackedObjPos = elementToTrack.getBoundingClientRect().top;
  if(trackedObjPos > 264)
  {
    bodyElement.style.cssText = '';
  }

Hy vọng câu trả lời này là hữu ích rộng rãi hơn bây giờ.


điều này rất có thể nằm ở đầu của rất nhiều câu trả lời khi bạn nhấp vào hoạt động để sắp xếp các câu trả lời
lucchi

@lucchi: D, tôi cho rằng tôi có thể xóa văn bản @ trên cùng ... mặc dù tôi chỉ xảy ra lần này và nhận ra rằng tôi đã tìm thấy một giải pháp tốt hơn cho vấn đề của riêng mình ... mặc dù câu trả lời của tôi là nhiều hơn một lưu ý chân để câu trả lời đầy đủ hơn. Tôi nghi ngờ các yêu cầu của tôi có thể không phổ biến đến mức các câu trả lời khác không phù hợp với tôi?
MER

1
Tôi đánh giá cao câu trả lời của bạn dù sao, nó chỉ để nói với bạn rằng, bất kỳ ai sẵn sàng sẽ nhận thức được rằng bạn đã viết một cái gì đó mới. Bạn không đơn độc ....
lucchi

5

Tôi đã làm nó như thế này để nó tương thích chéo với các trình duyệt cũ.

// For really old browser's or incompatible ones
    function getOffsetSum(elem) {
        var top = 0,
            left = 0,
            bottom = 0,
            right = 0

         var width = elem.offsetWidth;
         var height = elem.offsetHeight;

        while (elem) {
            top += elem.offsetTop;
            left += elem.offsetLeft;
            elem = elem.offsetParent;
        }

         right = left + width;
         bottom = top + height;

        return {
            top: top,
            left: left,
            bottom: bottom,
            right: right,
        }
    }

    function getOffsetRect(elem) {
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docElem = document.documentElement;

        var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

        var clientTop = docElem.clientTop;
        var clientLeft = docElem.clientLeft;


        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;
        var bottom = top + (box.bottom - box.top);
        var right = left + (box.right - box.left);

        return {
            top: Math.round(top),
            left: Math.round(left),
            bottom: Math.round(bottom),
            right: Math.round(right),
        }
    }

    function getOffset(elem) {
        if (elem) {
            if (elem.getBoundingClientRect) {
                return getOffsetRect(elem);
            } else { // old browser
                return getOffsetSum(elem);
            }
        } else
            return null;
    }

Tìm hiểu thêm về tọa độ trong JavaScript tại đây: http://javascript.info/tutorial/coordins


4

Để có được tổng bù của một phần tử, bạn có thể tổng hợp đệ quy tất cả các giá trị cha mẹ:

function getParentOffsets(el): number {
if (el.offsetParent) {
    return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
    return 0;
}
}

với chức năng tiện ích này, tổng bù trên cùng của phần tử dom là:

el.offsetTop + getParentOffsets(el);

3
/**
 *
 * @param {HTMLElement} el
 * @return {{top: number, left: number}}
 */
function getDocumentOffsetPosition(el) {
    var position = {
        top: el.offsetTop,
        left: el.offsetLeft
    };
    if (el.offsetParent) {
        var parentPosition = getDocumentOffsetPosition(el.offsetParent);
        position.top += parentPosition.top;
        position.left += parentPosition.left;
    }
    return position;
}

Cảm ơn ThinkingStiff cho câu trả lời , đây chỉ là một phiên bản của nó.


2

Tôi đã sử dụng thành công giải pháp của Andy E để định vị phương thức bootstrap 2 tùy thuộc vào liên kết nào trong một hàng trong bảng mà người dùng nhấp vào. Trang này là trang Tapestry 5 và javascript bên dưới được nhập vào lớp trang java.

javascript:

function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset   = elemRect.top - bodyRect.top;
offset   = offset + 20;
$('#serviceLineModal').css("top", offset);

}

Mã phương thức của tôi:

<div id="serviceLineModal" class="modal hide fade add-absolute-position" data-backdrop="static" 
 tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="top:50%;">
<div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
    <h3 id="myModalLabel">Modal header</h3>
</div>

<div class="modal-body">
    <t:zone t:id="modalZone" id="modalZone">
        <p>You selected service line number: ${serviceLineNumberSelected}</p>
    </t:zone>
</div>

<div class="modal-footer">
    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <!-- <button class="btn btn-primary">Save changes</button> -->
</div>

Liên kết trong vòng lặp:

<t:loop source="servicesToDisplay" value="service" encoder="encoder">
<tr style="border-right: 1px solid black;">       
    <td style="white-space:nowrap;" class="add-padding-left-and-right no-border"> 
        <a t:type="eventLink" t:event="serviceLineNumberSelected" t:context="service.serviceLineNumber" 
            t:zone="pageZone" t:clientId="modalLink${service.serviceLineNumber}"
            onmouseover="setLinkPosition(this);">
            <i class="icon-chevron-down"></i> <!-- ${service.serviceLineNumber} -->
        </a>
    </td>

Và mã java trong lớp trang:

void onServiceLineNumberSelected(String number){
    checkForNullSession();
    serviceLineNumberSelected = number;
    addOpenServiceLineDialogCommand();
    ajaxResponseRenderer.addRender(modalZone);
}

protected void addOpenServiceLineDialogCommand() {
    ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
        @Override
        public void run(JavaScriptSupport javascriptSupport) {
            javascriptSupport.addScript("$('#serviceLineModal').modal('show');");
        }
    });
}

Hy vọng điều này sẽ giúp ai đó, bài viết này đã giúp đỡ.


Mã js này đã giúp tôi, tôi có một ứng dụng webforms cũ bằng cách sử dụng nhiều trang giữ chỗ .aspx và tôi không thể tìm ra cách appendChild () của hộp bật lên mà tôi đang tạo để đưa nó vào chế độ xem vì toàn bộ cây DOM với nhiều chỗ dành sẵn và đánh dấu không chính xác. Sử dụng body.getBoundingClientRect () sau đó sử dụng định vị hàng đầu của nó là điều tôi cần. Cảm ơn.
Brad Martin

1

Sau nhiều nghiên cứu và thử nghiệm, nó dường như hoạt động

function getPosition(e) {
    var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') == -1);
    var x = 0, y = 0;
    while (e) {
        x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
        y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
        e = e.offsetParent;
    }
    return { x: x + window.scrollX, y: y + window.scrollY };
}

xem http://jsbin.com/xuvovalifo/edit?html,js,output


1

Chỉ cần nghĩ rằng tôi cũng ném nó ra khỏi đó.
Tôi chưa thể kiểm tra nó trong các trình duyệt cũ hơn, nhưng nó hoạt động trong phần mới nhất của top 3. :)

Element.prototype.getOffsetTop = function() {
    return ( this.parentElement )? this.offsetTop + this.parentElement.getOffsetTop(): this.offsetTop;
};
Element.prototype.getOffsetLeft = function() {
    return ( this.parentElement )? this.offsetLeft + this.parentElement.getOffsetLeft(): this.offsetLeft;
};
Element.prototype.getOffset = function() {
    return {'left':this.getOffsetLeft(),'top':this.getOffsetTop()};
};

0

Vì các trình duyệt khác nhau được kết xuất đường viền, phần đệm, lề và vv theo cách khác nhau. Tôi đã viết một hàm nhỏ để truy xuất các vị trí trên cùng và bên trái của phần tử cụ thể trong mọi phần tử gốc mà bạn muốn theo thứ nguyên chính xác:

function getTop(root, offset) {
    var rootRect = root.getBoundingClientRect();
    var offsetRect = offset.getBoundingClientRect();
    return offsetRect.top - rootRect.top;
}

Để lấy lại vị trí bên trái, bạn phải quay lại:

    return offsetRect.left - rootRect.left;

-1

Nhận vị trí của div đối với bên trái và trên cùng

  var elm = $('#div_id');  //get the div
  var posY_top = elm.offset().top;  //get the position from top
  var posX_left = elm.offset().left; //get the position from left 

Tôi đã bỏ phiếu vì dưới cùng và bên phải không phải là thuộc tính của offset ()
MacroMan

@MacroMan, tôi tôn trọng quyết định của bạn nhưng tôi đã giải thích trong văn bản rằng "phần dưới và bên phải mã không được kiểm tra." Ngoài ra, tôi sẽ cập nhật mã của tôi rất sớm.
Vishal

Tôi nghĩ đó là el.offsetTopel.offsetLeft(OP không nói gì về jQuery ... Tôi không chắc tại sao câu trả lời của bạn lại sử dụng jQuery ...)
mesqueeb
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.