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

Waleed Eissa
sumber
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;
Himanshu P
sumber
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.

Fabio Nolasco
sumber
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 .

Derek Swingley
sumber
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;
GURU PRASAD
sumber
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.

Szere Dyeri
sumber
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
};
jordancooperman
sumber
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;
}
rism
sumber
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

karlsebal
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.

Waleed Eissa
sumber
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);
Sunil
sumber
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();
Arif
sumber