Bagaimana cara mendapatkan posisi teratas elemen relatif terhadap viewport browser?


173

Saya ingin mendapatkan posisi elemen relatif ke viewport browser (viewport di mana halaman ditampilkan, bukan seluruh halaman). Bagaimana ini bisa dilakukan dalam JavaScript?

Terimakasih banyak


2
Saya pikir pertanyaan ini mirip dengan stackoverflow.com/questions/211703/… yang memiliki solusi yang sangat bagus.
Jakob Runge

1
@ JakobRunge, Itu "bagus" di tahun 2013?
Iulian Onofrei

1
Pertimbangkan untuk menerima solusi.
Iulian Onofrei

Jawaban:


306

Jawaban yang ada sekarang sudah usang. getBoundingClientRect()Metode asli telah ada selama beberapa waktu sekarang, dan melakukan persis apa yang ditanyakan. Plus itu didukung di semua browser (termasuk IE 5, tampaknya!)

Dari halaman MDN :

Nilai yang dikembalikan adalah objek TextRectangle, yang berisi properti read-only left, top, right, dan bottom yang menggambarkan kotak batas, dalam piksel, dengan relatif kiri atas ke kiri atas viewport .

Anda menggunakannya seperti ini:

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

3
Ini mungkin tidak berfungsi dengan benar ketika menggunakan zoom browser (Android Chrome). Solusi dari @rism ( lihat di bawah ) berfungsi dalam kasus ini
Dmitry

4
Bagus untuk sebagian besar kasus, tetapi mengasumsikan elemen tidak ada di dalam iframe tertanam, bukan? Pertanyaannya adalah menanyakan tentang relatif jendela browser. El.getBoundingClientRect () akan mengembalikan relatif jendela iframe, bukan jendela browser.
cakidnyc

6
Pertanyaannya adalah menanyakan tentang relatif jendela browser.
Dmitry Dvornikov

2
@ Denilson getBoundingClientRect().toptidak di IE11 saya mengembalikan 0. Apakah Anda punya solusi.
Arif

@Arif: Maaf, tapi saya tidak bisa mereproduksi masalah Anda. Saya menguji ini pada IE11 dan masih mendapat hasil: codepen.io/denilsonsa/pen/ypqyXj
Denilson Sá Maia

57

Pada kasus saya, agar aman mengenai pengguliran, saya menambahkan window.scroll ke persamaan:

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

Itu memungkinkan saya untuk mendapatkan posisi relatif elemen pada dokumen, bahkan jika sudah digulir.


2
Bukankah Anda seharusnya menambahkan posisi gulir bukannya mengurangi itu?
Iulian Onofrei

Saya mengangkat jawabannya tetapi tetap saja, @lulian Onofrei benar. Harap edit.
pmrotule

@Fabio getBoundingClientRect().toptidak di IE11 saya mengembalikan 0. Apakah Anda punya solusi.
Arif

1
@Arif window.scrollY || window.pageYOffsetdapat meningkatkan dukungan
Fabian von Ellerts

@FabianvonEllerts :)
Arif

15

Sunting: Tambahkan beberapa kode ke akun untuk pengguliran halaman.

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

Argumen id adalah id elemen yang offsetnya Anda inginkan. Diadaptasi dari pos quirksmode .


1
Terima kasih atas jawaban Anda, tetapi saya pikir Anda salah memahami pertanyaan saya, saya tahu cara mendapatkan bagian atas elemen relatif ke halaman, yang saya coba lakukan adalah mendapatkan posisi relatif terhadap 'viewport' (mis. Jendela browser , bukan dokumen)
Waleed Eissa

Maaf, penggunaan 'viewport' Anda membuat saya marah. Jadi, Anda mengatakan ingin akun chrome browser, yaitu bilah bookmark, bilah lokasi, dll?
Derek Swingley

Tidak, hanya jendela tempat Anda melihat halaman. Jika halaman digulir, ini akan berbeda dari bagian atas dokumen, jika tidak mereka sama.
Waleed Eissa

Oh oke, saya mengerti. Tidak yakin bagaimana melakukan itu ... akan mengedit jawaban saya jika saya menemukan sesuatu.
Derek Swingley

Saya membuat kode di atas sebuah jelek sedikit tetapi bekerja ... penggunaan offsetParent.scrollTop
Derek Swingley

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

Ini sepertinya menghitung dari atas dokumen, bukan dari viewport
Scott Leonard

6

Anda dapat mencoba:

node.offsetTop - window.scrollY

Ini bekerja di Opera dengan tag meta viewport ditentukan.


7
Jika saya memahami dokumentasi dengan benar, offsetTopitu relatif terhadap elemen posisi terdekat. Yang, tergantung pada struktur halaman, mungkin atau mungkin bukan elemen root.
Denilson Sá Maia

5

jQuery mengimplementasikan ini dengan sangat elegan. Jika Anda melihat sumber untuk jQuery offset, Anda akan menemukan ini pada dasarnya bagaimana ini diterapkan:

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

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

2

Fungsi pada halaman ini akan mengembalikan persegi panjang dengan koordinat atas, kiri, tinggi dan lebar dari elemen yang disahkan relatif ke port tampilan browser.

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

sumber


1

Terima kasih atas semua jawabannya. Tampaknya Prototipe sudah memiliki fungsi yang melakukan fungsi ini (halaman ()). Dengan melihat kode sumber fungsi, saya menemukan bahwa pertama-tama menghitung posisi elemen offset relatif terhadap halaman (yaitu bagian atas dokumen), kemudian kurangi scrollTop dari itu. Lihat kode sumber prototipe untuk lebih jelasnya.


Keputusan bagus. Mungkin yang terbaik untuk pergi dengan salah satu perpustakaan utama karena mereka lebih cenderung memberikan hasil yang sama di seluruh browser. Jika Anda penasaran, periksa jawaban saya. Kode di sana akan melakukan ini, tetapi saya tidak tahu bagaimana itu akan bertahan di browser.
Derek Swingley

1

Saya mengasumsikan elemen yang memiliki id btn1ada di halaman web, dan juga bahwa jQuery disertakan. Ini telah berfungsi di semua browser modern Chrome, FireFox, IE> = 9 dan Edge. jQuery hanya digunakan untuk menentukan posisi relatif terhadap dokumen.

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 adalah JavaScript. Saya yakin maksud Anda "ini bukan 100% vanilla / JS murni."
hungerstar

Ya, itulah yang saya maksud.
Sunil

1

Terkadang getBoundingClientRect()nilai properti objek menunjukkan 0 untuk IE. Dalam hal ini Anda harus mengatur display = 'block'elemen. Anda dapat menggunakan kode di bawah ini untuk semua browser untuk mendapatkan offset.

Perpanjang fungsi 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));

Gunakan:

var elementOffset = $("#" + elementId).weOffset();
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.