Ambil posisi (X, Y) elemen HTML relatif terhadap jendela browser

1570

Saya ingin tahu cara mendapatkan posisi X dan Y elemen HTML seperti imgdan divdalam JavaScript relatif ke jendela browser.

Mendongkrak
sumber
134
Relatif dengan apa?
AnthonyWJones
4
Saya telah menggunakan 7 baris kode ini yang berfungsi di semua browser dengan perbedaan dalam ie5,6,7 (saya tidak ingat memiliki masalah ... mungkin jenis dokumen) ... quirksmode.org/js/findpos. html dan saya sudah sering menggunakannya selama bertahun-tahun. mungkin sombody dapat menunjukkan kekurangannya jika ada.
Jayapal Chandran
99
@AnthonyWJones, saya kira Anda membutuhkan kesenangan pedantic, tetapi jelas, karena selalu ketika tidak ditentukan, bahwa OP mengacu pada kasus yang paling umum, atau mengacu pada koordinat jendela browser.
Motes
7
@Mote, Tidak, tidak terlalu jelas. Tinggalkan inferensi, subjektivitas dan aksioma palsu. Ini bisa relatif ke viewport atau bagian atas halaman (alias document.documentElement).
Andre Figueiredo
16
Default ke yang paling umum bukan inferensi, itu adalah perkembangan logis, sehingga itu akan relatif ke jendela, atau "bagian atas halaman" mungkin istilah seperti yang Anda katakan. Dalam Game Theory, ini dikodifikasikan sebagai konsep yang disebut titik Schelling. Pastikan untuk menentukan kapan Anda tidak bermaksud kasus yang paling umum.
Motes

Jawaban:

1796

Pendekatan yang benar adalah dengan menggunakan element.getBoundingClientRect():

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

Internet Explorer telah mendukung ini sejak Anda cenderung peduli dan akhirnya standar di Tampilan CSSOM . Semua browser lain sudah lama menggunakannya .

Beberapa browser juga mengembalikan properti tinggi dan lebar, meskipun ini tidak standar. Jika Anda khawatir tentang kompatibilitas browser yang lebih lama, periksa revisi jawaban ini untuk penerapan penurunan yang dioptimalkan.

Nilai yang dikembalikan element.getBoundingClientRect()relatif terhadap viewport. Jika Anda membutuhkannya relatif terhadap elemen lain, cukup kurangi satu persegi panjang dari yang lain:

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

alert('Element is ' + offset + ' vertical pixels from <body>');
Andy E
sumber
143
Artikel yang sangat menarik tentang koordinat JS . Artikel ini tidak disebutkan di SO, seharusnya ..
e2-e4
6
Tidak berfungsi ... itu 8 piksel keluar secara vertikal dan horizontal, karena margin 8px dalam tubuh. Solusi Meouw bekerja dengan sempurna. Jika Anda mau, saya memiliki tes kecil untuk menunjukkan masalahnya.
CpnCrunch
3
Sebenarnya, saya pikir itu tergantung pada apa yang sebenarnya ingin Anda dapatkan dari koordinasi. Pertanyaannya sedikit ambigu. Jawaban Anda benar jika Anda hanya menginginkan koordinat relatif terhadap viewport atau relatif terhadap elemen tubuh, tetapi itu tidak membantu Anda jika Anda menginginkan posisi absolut elemen (yang saya bayangkan adalah apa yang diinginkan kebanyakan orang) . jawaban meouw juga tidak memberi Anda hal itu, kecuali jika Anda menghapus bit '-scrollLeft' dan '-scrollTop'.
CpnCrunch
4
@CpnCrunch: Saya mengerti maksud Anda sekarang; Saya tidak menyadari bahwa Anda mengacu pada bagian terakhir dari jawaban saya. Ketika tubuh memiliki margin, kotak pembatasnya akan diimbangi dengan jumlah piksel tersebut di masing-masing sisi. Dalam hal ini, Anda juga harus mengurangi gaya margin yang dihitung dari koordinat tubuh. Perlu dicatat bahwa reset CSS menghapus margin dari <body>, meskipun.
Andy E
3
element.getBoundingClientRect().toptidak mengembalikan offset atas yang benar jika ada position: fixedelemen di iOS (iPhone 8) jika mengandung elemen input terfokus dan keyboard virtual telah meluncur. Misalnya, jika offset aktual dari bagian atas jendela adalah 200px, itu akan melaporkannya sebagai 420px, di mana 220px adalah ketinggian keyboard virtual. Jika Anda mengatur elemen position: absolutedan memposisikannya pada posisi yang sama seperti jika diperbaiki, maka Anda akan mendapatkan 200px yang benar. Saya tidak tahu solusi untuk ini.
Jānis Elmeris
327

Perpustakaan berusaha keras untuk mendapatkan offset yang akurat untuk suatu elemen.
inilah fungsi sederhana yang melakukan pekerjaan dalam setiap keadaan yang telah saya coba.

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; 
meouw
sumber
14
ubah: el = el.parentNode ke: el = el.offsetParent; dan tampaknya berfungsi untuk iframe bersarang sekarang ... Saya pikir itu yang Anda maksud?
Adam
4
Solusi ini tidak lengkap. Coba letakkan batas tebal pada div dan bersarang beberapa kali. Dalam versi apa pun dari Firefox (2-5) akan dimatikan oleh lebar perbatasan (bahkan dalam mode kepatuhan standar).
ck_
3
tidakkah ada cara yang lebih bersih untuk melakukan itu, seperti fungsi el.totalOffsetLeft dan totalOffsetTop? jQuery tentu saja memiliki opsi ini tetapi sayang sekali tidak ada metode resmi untuk ini, apakah kita perlu menunggu DOM4? Saya sedang membangun aplikasi kanvas HTML5, jadi saya pikir solusi terbaik adalah dengan memasukkan elemen kanvas dalam iframe sehingga saya bisa mendapatkan koordinat nyata dengan clientX dan clientY ketika kita mengklik elemen.
baptx
1
Jadi, @Adam dan meouw, apakah Anda mengatakan blok kode pertama salah? Lalu mengapa tidak menghapus itu sama sekali? (Pertanyaan ini telah dilihat beberapa pemirsa selama bertahun-tahun; mungkin menyenangkan untuk menghapus hal-hal jika mereka salah?)
Arjan
2
@aptx: Saya tahu ini adalah jawaban yang terlambat, tetapi saya tidak melihat komentar Anda lebih awal. Lihat jawaban saya di bawah ini untuk alternatif yang lebih sesuai standar - Anda bahkan tidak benar-benar membutuhkan kode cadangan, karena browser telah mendukungnya sejak lama.
Andy E
242

Ini bekerja untuk saya (dimodifikasi dari jawaban pilihan tertinggi):

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

Dengan ini kita bisa menelepon

getOffset(element).left

atau

getOffset(element).top
Adam Grant
sumber
1
Hanya solusi ini yang bekerja untuk saya ketika elemen ditempatkan di divyang berpusat menggunakan css top:50%, left:50%; transform:translate(-50%, -50%);... sangat baik. Setuju dengan pertanyaan @ScottBiggs. Logis adalah mencari kesederhanaan.
nelek
3
mungkin itu bukan pemenang karena sama dengan solusi Andy E, tetapi tidak berfungsi di browser lama <IE9
niltoid
getBoundingClientRect menyebabkan lonjakan
pengecatan
1
Anda harus mendefinisikan rectmenggunakan var, letatau const. Kalau tidak, itu didefinisikan sebagai window.rect.
hev1
2
left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2)akan mengembalikan posisi tengah elemen
abhishake
97

Jika Anda ingin melakukannya hanya dalam javascript , berikut adalah beberapa baris yang digunakangetBoundingClientRect()

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

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

Baris pertama akan mengembalikan offsetTopsay Y relatif ke dokumen. Baris kedua akan mengembalikan offsetLeftkatakan X relatif terhadap dokumen.

getBoundingClientRect() adalah fungsi javascript yang mengembalikan posisi elemen relatif ke viewport jendela.

Mustkeem K
sumber
2
Ini harus menjadi solusinya. Tidak ada kode panjang bs di sini, dapat dimengerti dan sangat akurat!
E Alexis MT
2
bagaimana jika elemen tersebut berada dalam elemen yang dapat digulir? Satu-satunya cara untuk melakukannya adalah dengan menghitung offset semua elemen induk, seperti yang disarankan oleh jawaban lain
Dmitri Pisarev
Seperti yang dikatakan Dmitri, ini tidak benar dan sangat cacat. Seharusnya tidak memiliki suara sebanyak yang ada. Ini akan SELALU berada dalam elemen yang dapat digulir juga karena dokumen itu sendiri dapat digulir. Dengan kata lain, kecuali seluruh dokumen cocok dengan viewport, itu akan selalu salah jika pengguna telah menggulir jendela utama sama sekali.
Im
46

Elemen HTML pada sebagian besar browser akan memiliki: -

offsetLeft
offsetTop

Ini menentukan posisi elemen relatif orang tua terdekatnya yang memiliki tata letak. Induk ini sering dapat diakses jika properti offsetParent.

IE dan FF3 miliki

clientLeft
clientTop

Properti ini kurang umum, mereka menentukan posisi elemen dengan area klien orang tuanya (area empuk adalah bagian dari area klien tetapi batas dan margin tidak).

AnthonyWJones
sumber
36

Jika halaman menyertakan - setidaknya- "DIV" apa saja, fungsi yang diberikan oleh meouw melempar nilai "Y" melampaui batas halaman saat ini. Untuk menemukan posisi yang tepat, Anda harus menangani offsetParent dan parentNode.

Coba kode yang diberikan di bawah ini (diperiksa untuk 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];
};
Refik Ayata
sumber
1
seperti dengan solusi lain bahkan dengan FF 24.4 kode di atas tidak berfungsi ketika ada batas lebar sebagai bagian dari tata letak posisi.
merampok
Anda seorang penyelamat. Saya sedang mengerjakan beberapa bug GWT yang cukup dalam sekarang dan melemparkan ini dalam metode JSNI menyelesaikan semua masalah saya !!
Will Byrne
35

Anda dapat menambahkan dua properti Element.prototypeuntuk mendapatkan bagian atas / kiri elemen apa pun.

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

Ini disebut seperti ini:

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

Berikut ini adalah demo yang membandingkan hasilnya dengan jQuery offset().topdan .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/

ThinkingStiff
sumber
14
memodifikasi Element.prototypeumumnya dianggap ide yang buruk . Itu menyebabkan sangat sulit untuk mempertahankan kode. Selain itu, kode ini tidak memperhitungkan pengguliran.
Jared Forsyth
23

Untuk mengambil posisi relatif terhadap halaman secara efisien, dan tanpa menggunakan fungsi rekursif: (termasuk IE juga)

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;
Abdul Rahim Haddad
sumber
Termasuk semua scrollTop / scrollLeft penting membuat jawaban ini sedikit lebih benar.
scunliffe
19

Bagaimana dengan sesuatu seperti ini, dengan melewati ID elemen dan itu akan mengembalikan kiri atau atas, kita juga dapat menggabungkan mereka:

1) temukan kiri

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

2) temukan top

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

atau 3) temukan kiri dan atas bersamaan

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');
Alireza
sumber
16

Anda mungkin lebih baik dilayani dengan menggunakan kerangka kerja JavaScript, yang memiliki fungsi untuk mengembalikan informasi tersebut (dan banyak lagi!) Dengan cara yang tidak tergantung pada browser. Berikut ini beberapa di antaranya:

Dengan kerangka kerja ini, Anda dapat melakukan sesuatu seperti: $('id-of-img').top untuk mendapatkan koordinat y-pixel dari gambar.

Shalom Craimer
sumber
2
@Costa Mereka semua menggunakan fungsi-fungsi semacam itu, meskipun ini adalah bidang yang berkembang, karena peramban yang berbeda sesuai dengan spesifikasi. Begitu banyak kode yang berhubungan dengan "memperbaiki" hal-hal yang salah. Anda benar-benar harus melihat kode sumbernya.
Shalom Craimer
18
@scraimer: jQuery dan YUI BUKAN kerangka kerja !!! Ini adalah perpustakaan ! Ini tidak sama. Mengutip jquery.com : "jQuery adalah perpustakaan JavaScript yang cepat, kecil, dan kaya fitur ." Mengutip yuilibrary.com (Anda juga dapat melihat kata "ajaib" di URL ...): "YUI adalah pustaka JavaScript dan CSS sumber terbuka gratis untuk membuat aplikasi web kaya interaktif."
Sk8erPeter
29
Jadi, Anda harus menggunakan perpustakaan besar hanya untuk tugas sederhana ini? Tidak perlu. Saya benci bahwa setiap kali saya ingin melakukan sesuatu dengan js, saya mendapatkan solusi jQuery ini. jQuery bukan JS!
Opptatt Jobber
1
@OpptattJobber Saya setuju ... JQuery bukan javascript. Itu bergantung pada Javascript tetapi bahasa pemrograman yang berbeda sama sekali.
Nick Manning
5
@NickManning Ini bukan bahasa baru, ini lapisan abstraksi untuk DOM yang menghilangkan keharusan untuk menulis kompatibilitas dan solusi kinerja langsung ke dalam kode Anda. Jika Anda tidak menggunakan jQuery, Anda harus menggunakan beberapa jenis lapisan abstraksi.
ginman
15

jQuery .offset () akan mendapatkan koordinat saat ini dari elemen pertama, atau mengatur koordinat setiap elemen, di set elemen yang cocok, relatif terhadap dokumen.

aliasuppi
sumber
Untuk beberapa alasan itu tidak memberikan hasil yang sama di IE dibandingkan dengan browser lain. Saya berpikir bahwa di IE memberikan posisi relatif ke jendela, jadi jika Anda menggulir, Anda akan mendapatkan hasil yang berbeda di IE dibandingkan dengan yang lain
Abdul Rahim Haddad
1
offset () dikacaukan oleh zoom, setidaknya di Android Chrome, jadi tidak ada gunanya. stackoverflow.com/a/11396681/1691517 menunjukkan cara yang toleran terhadap zoom.
Timo Kähkönen
10

Saya telah mengambil jawaban @ meouw, ditambahkan di clientLeft yang memungkinkan untuk perbatasan, dan kemudian membuat tiga versi:

getAbsoluteOffsetFromBody - mirip dengan @ meouw, ini mendapatkan posisi absolut relatif terhadap elemen tubuh atau html dokumen (tergantung pada mode quirks)

getAbsoluteOffsetFromGivenElement - mengembalikan posisi absolut relatif terhadap elemen yang diberikan (relatifEl). Perhatikan bahwa elemen yang diberikan harus mengandung elemen el, atau ini akan berperilaku sama dengan getAbsoluteOffsetFromBody. Ini berguna jika Anda memiliki dua elemen yang terkandung di dalam elemen (dikenal) lainnya (opsional beberapa node di atas simpul pohon) dan ingin menjadikannya posisi yang sama.

getAbsoluteOffsetFromRelative - mengembalikan posisi absolut relatif ke elemen induk pertama dengan posisi: relatif. Ini mirip dengan getAbsoluteOffsetFromGivenElement, untuk alasan yang sama tetapi hanya akan sejauh elemen pencocokan pertama.

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

Jika Anda masih mengalami masalah, terutama yang berkaitan dengan pengguliran, Anda dapat mencoba melihat http://www.greywyvern.com/?post=331 - Saya perhatikan setidaknya satu potong kode yang dipertanyakan di getStyle yang seharusnya baik-baik saja dengan asumsi browser berperilaku , tetapi belum menguji sisanya sama sekali.

James Carlyle-Clarke
sumber
Menarik ... tetapi di Edge getAbsoluteOffsetFromBody (elemen) .top mengembalikan nilai yang berbeda saat saya menggulir, di Chrome itu tetap konsisten saat saya menggulir. Sepertinya Edge menyesuaikan dengan posisi gulir. Ketika elemen digulir ke luar dari tampilan saya mendapatkan nilai negatif.
Norman
getAbsoluteOffsetFromRelative membuat hari saya, saya harus mereproduksi tooltip bootstrap tanpa Javascript dari bootstrap, itu banyak membantu saya.
Nolyurn
Untuk jaga-jaga jika Meouw mengubah nama pengguna mereka, tautan untuk menjawab: stackoverflow.com/a/442474/3654837 . (Saya tidak ingin menabrak utas lama ini jadi ya, saya memberi komentar)
Mukyuu
8

jika menggunakan jQuery, plugin dimensi sangat bagus dan memungkinkan Anda menentukan dengan tepat apa yang Anda inginkan.

misalnya

Posisi relatif, posisi absolut, posisi absolut tanpa bantalan, dengan bantalan ...

Terus, katakan saja ada banyak yang bisa Anda lakukan dengannya.

Plus bonus menggunakan jQuery adalah ukuran file yang ringan dan mudah digunakan, Anda tidak akan kembali ke JavaScript tanpa itu sesudahnya.

John_
sumber
8

Ini adalah kode terbaik yang berhasil saya buat (berfungsi di iframe juga, tidak seperti offset jQuery ()). Sepertinya webkit memiliki sedikit perilaku yang berbeda.

Berdasarkan komentar 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 };
}
Ron Reiter
sumber
Perhatikan bahwa Anda harus jQuerymenggunakan $.browser.webkit; Anda harus bermain-main dengan navigator.userAgentuntuk melakukan hal yang sama dengan murni JavaScript.
SP
2
$ .browser tidak lagi tersedia dalam versi terbaru jQuery
Gus
sayangnya bahkan dengan FF 24.4 kode di atas tidak berfungsi ketika ada batas lebar sebagai bagian dari tata letak positioning.
merampok
8

Jika Anda menggunakan jQuery, ini bisa menjadi solusi sederhana:

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

Perbedaan antara kecil dan kecil

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

Lihat contoh koordinat: http://javascript.info/tutorial/coordinates

KingRider
sumber
Sayangnya ini tidak berfungsi untuk semua elemen yang mungkin ditemukan di dalam dokumen HTML modern. Tertanam <svg>elemen, misalnya, tidak memiliki offsetLeft, offsetTop, dan offsetParentproperti.
Jonathan Eunice
Hanya DIVatau IMGtidak SVG. Lihat "Lihat contoh koordinat"? Anda dapat menemukan objek svg adalah panggilan posisi getOffsetRect , mencoba dan bergantung pada objek.
KingRider
7

Pendekatan terbersih yang saya temukan adalah versi sederhana dari teknik yang digunakan oleh jQuery offset. Mirip dengan beberapa jawaban lain yang dimulai dengan getBoundingClientRect; kemudian menggunakan windowdan documentElementuntuk menyesuaikan posisi gulir serta hal-hal seperti margin pada body(seringkali default).

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

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

James Montagne
sumber
6

Meskipun ini sangat mungkin hilang di bagian bawah dari begitu banyak jawaban, solusi teratas di sini tidak bekerja untuk saya.
Sejauh yang saya tahu, tidak ada jawaban lain yang akan membantu.

Situasi :
Di halaman HTML5 saya memiliki menu yang merupakan elemen nav di dalam header (bukan header tetapi header di elemen lain).
Saya ingin navigasi untuk tetap di atas setelah pengguna menggulir ke sana, tapi sebelumnya header ini diposisikan absolut (jadi saya bisa sedikit overlay sesuatu yang lain).
Solusi di atas tidak pernah memicu perubahan karena .offsetTop tidak akan berubah karena ini adalah elemen yang diposisikan mutlak. Selain itu, properti .scrollTop hanyalah bagian atas elemen paling atas ... yaitu 0 dan selalu 0.
Setiap tes yang saya lakukan menggunakan kedua (dan sama dengan hasil getBoundingClientRect) tidak akan memberi tahu saya jika bagian atas bilah navigasi pernah menggulir ke bagian atas halaman yang dapat dilihat (sekali lagi, seperti yang dilaporkan dalam konsol, mereka hanya tinggal nomor yang sama saat menggulir terjadi).

Solusi
Solusi bagi saya adalah memanfaatkan

window.visualViewport.pageTop

Nilai properti pageTop mencerminkan bagian layar yang dapat dilihat, oleh karena itu memungkinkan saya untuk melacak di mana elemen mengacu pada batas-batas area yang dapat dilihat.

Mungkin tidak perlu dikatakan, kapan pun saya berurusan dengan pengguliran, saya berharap untuk menggunakan solusi ini untuk merespons secara sistematis terhadap pergerakan elemen yang sedang digulir.
Semoga ini bisa membantu orang lain.
CATATAN PENTING: Ini tampaknya berfungsi di Chrome dan Opera saat ini & pasti tidak di Firefox (6-2018) ... sampai Firefox mendukung visualViewport Saya sarankan TIDAK menggunakan metode ini, (dan saya harap mereka segera melakukannya ... itu membuat banyak lebih masuk akal daripada yang lain).


UPDATE:
Hanya sebuah catatan mengenai solusi ini.
Sementara saya masih menemukan apa yang saya temukan sangat berharga untuk situasi di mana "... terprogram merespons pergerakan elemen yang sedang digulir." berlaku. Solusi yang lebih baik untuk masalah yang saya miliki adalah menggunakan CSS untuk mengatur posisi: stickypada elemen. Menggunakan sticky Anda dapat memiliki elemen tetap di atas tanpa menggunakan javascript (CATATAN: ada kalanya ini tidak akan bekerja seefektif mengubah elemen untuk diperbaiki tetapi untuk sebagian besar menggunakan pendekatan sticky kemungkinan akan lebih unggul)

UPDATE01:
Jadi saya menyadari bahwa untuk halaman yang berbeda saya memiliki persyaratan di mana saya perlu mendeteksi posisi elemen dalam pengaturan pengguliran yang agak rumit (paralaks plus elemen yang menggulir melewati sebagai bagian dari pesan). Saya menyadari dalam skenario itu bahwa berikut ini memberikan nilai yang saya gunakan untuk menentukan kapan harus melakukan sesuatu:

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

Semoga jawaban ini lebih bermanfaat sekarang.

MER
sumber
ini sangat mungkin berada di bagian atas dari begitu banyak jawaban ketika Anda mengklik aktif untuk mengurutkan jawaban
lucchi
@ Lucchi: D yah saya kira saya bisa menghapus teks @ atas ... meskipun saya baru saja terjadi pada yang satu ini dan menyadari bahwa saya telah menemukan solusi yang lebih baik untuk masalah saya sendiri ... meskipun jawaban saya lebih dari satu catatan kaki untuk jawaban yang lebih lengkap. Saya menduga persyaratan saya mungkin tidak umum sehingga jawaban yang lain tidak berhasil untuk saya?
MER
1
Saya menghargai jawaban Anda, itu hanya untuk memberi tahu Anda bahwa, siapa pun yang mau akan sadar bahwa Anda menulis sesuatu yang baru. Anda tidak sendirian ....
lucchi
5

Saya melakukannya seperti ini sehingga kompatibel dengan browser lama.

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

Lebih lanjut tentang koordinat dalam JavaScript di sini: http://javascript.info/tutorial/coordinates

Bojan Kseneman
sumber
4

Untuk mendapatkan total offset elemen, Anda bisa meringkas semua offset induk secara rekursif:

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

dengan fungsi utilitas ini offset total atas elemen dom adalah:

el.offsetTop + getParentOffsets(el);
Kevin K.
sumber
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;
}

Terima kasih ThinkingStiff atas jawabannya , ini hanya versi lain.

Văn Quyết
sumber
2

Saya berhasil menggunakan solusi Andy E untuk memposisikan modal bootstrap 2 tergantung pada tautan apa di baris tabel yang diklik pengguna. Halaman ini adalah halaman Tapestry 5 dan javascript di bawah ini diimpor dalam kelas halaman 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);

}

Kode modal saya:

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

Tautan di loop:

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

Dan kode java di kelas halaman:

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

Semoga ini bisa membantu seseorang, posting ini membantu.

Mark Espinoza
sumber
Kode js ini membantu saya, saya memiliki aplikasi webform lama menggunakan banyak placeholder menyuntikkan halaman .aspx dan saya tidak tahu bagaimana cara menambahkanChild () dari kotak popup yang saya buat untuk memasukkannya ke dalam viewport karena seluruh pohon DOM rusak. dengan beberapa placeholder dan markup yang salah. Menggunakan body.getBoundingClientRect () lalu menggunakan topinya dalam penentuan posisi adalah yang saya butuhkan. Terima kasih.
Brad Martin
1

Setelah banyak penelitian dan pengujian, ini tampaknya berhasil

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

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

kode kernow
sumber
1

Hanya berpikir saya akan membuang ini di sana juga.
Saya belum bisa mengujinya di browser yang lebih lama, tetapi ia bekerja di yang terbaru dari atas 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()};
};
Duncan
sumber
0

Karena browser yang berbeda memberikan batas, padding, margin, dll dengan cara yang berbeda. Saya menulis sedikit fungsi untuk mengambil posisi atas dan kiri elemen tertentu di setiap elemen root yang Anda inginkan dalam dimensi yang tepat:

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

Untuk mengambil posisi kiri, Anda harus kembali:

    return offsetRect.left - rootRect.left;
samadadi
sumber
-1

Dapatkan posisi div sehubungan dengan kiri dan Atas

  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 
Vishal
sumber
Saya memilih sebagai bawah dan kanan bukan sifat offset ()
MacroMan
@ MacroMan, saya menghormati keputusan Anda tetapi saya sudah menjelaskan dalam teks bahwa "kode di bagian bawah dan kanan tidak diuji.". Juga, saya akan segera memperbarui kode saya.
Vishal
Saya pikir itu el.offsetTopdan el.offsetLeft(OP tidak mengatakan apa pun tentang jQuery ... Saya juga tidak yakin mengapa jawaban Anda menggunakan jQuery ...)
mesqueeb