Menggunakan jquery untuk mendapatkan posisi elemen relatif terhadap viewport


117

Apa cara yang tepat untuk mendapatkan posisi elemen di halaman relatif terhadap viewport (bukan dokumen). jQuery.offsetfungsinya tampak menjanjikan:

Dapatkan koordinat saat ini dari elemen pertama, atau setel koordinat setiap elemen, dalam kumpulan elemen yang cocok, relatif terhadap dokumen.

Tapi itu relatif terhadap dokumennya. Apakah ada metode setara yang mengembalikan offset relatif terhadap viewport?


5
CATATAN: Lihat jawaban @Igor G ...
Carl Smith

3
Untuk DA, Anda harus benar-benar menetapkan jawaban Igor G sebagai diterima, ini adalah penyelamat!
Vincent Duprez

Jawaban:



287

Cara termudah untuk menentukan ukuran dan posisi elemen adalah dengan memanggil metode getBoundingClientRect () . Metode ini mengembalikan posisi elemen dalam koordinat viewport. Ia mengharapkan tidak ada argumen dan mengembalikan objek dengan properti kiri, kanan, atas , dan bawah . Properti kiri dan atas memberikan koordinat X dan Y dari sudut kiri atas elemen dan properti kanan dan bawah memberikan koordinat sudut kanan bawah.

element.getBoundingClientRect(); // Get position in viewport coordinates

Didukung di mana-mana.


15
Sungguh luar biasa metode ini ditambahkan oleh IE5 ... ketika sesuatu itu bagus, itu bagus!
roy riojas

Ini tampak hebat pada awalnya tetapi tidak memperhitungkan pengguna yang melakukan zoom di Mobile Safari 7.
MyNameIsKo

2
Tidak didukung oleh firefox terbarugetBoundingClientRect is not a function
pengguna007

2
@ user007 Saya mengonfirmasi bahwa ini didukung oleh firefox terbaru.
adriendenat

26
Jawaban yang bagus, dan untuk membuatnya jquery cukup lakukan dengan cara ini: $('#myElement')[0].getBoundingClientRect().top(atau posisi lain)
Guillaume Arluison

40

Berikut dua fungsi untuk mendapatkan tinggi halaman dan jumlah scroll (x, y) tanpa menggunakan plugin dimensi (membengkak):

// getPageScroll() by quirksmode.com
function getPageScroll() {
    var xScroll, yScroll;
    if (self.pageYOffset) {
      yScroll = self.pageYOffset;
      xScroll = self.pageXOffset;
    } else if (document.documentElement && document.documentElement.scrollTop) {
      yScroll = document.documentElement.scrollTop;
      xScroll = document.documentElement.scrollLeft;
    } else if (document.body) {// all other Explorers
      yScroll = document.body.scrollTop;
      xScroll = document.body.scrollLeft;
    }
    return new Array(xScroll,yScroll)
}

// Adapted from getPageSize() by quirksmode.com
function getPageHeight() {
    var windowHeight
    if (self.innerHeight) { // all except Explorer
      windowHeight = self.innerHeight;
    } else if (document.documentElement && document.documentElement.clientHeight) {
      windowHeight = document.documentElement.clientHeight;
    } else if (document.body) { // other Explorers
      windowHeight = document.body.clientHeight;
    }
    return windowHeight
}

Ini brilian. Sangat berguna.
Jimmy

Karena penasaran, mengapa Anda menggunakan properti "diri" dan bukan jendela dalam kasus ini?
dkugappi


23

jQuery.offsetperlu digabungkan dengan scrollTopdan scrollLeftseperti yang ditunjukkan pada diagram ini:

scroll viewport dan offset elemen

Demo:

function getViewportOffset($e) {
  var $window = $(window),
    scrollLeft = $window.scrollLeft(),
    scrollTop = $window.scrollTop(),
    offset = $e.offset(),
    rect1 = { x1: scrollLeft, y1: scrollTop, x2: scrollLeft + $window.width(), y2: scrollTop + $window.height() },
    rect2 = { x1: offset.left, y1: offset.top, x2: offset.left + $e.width(), y2: offset.top + $e.height() };
  return {
    left: offset.left - scrollLeft,
    top: offset.top - scrollTop,
    insideViewport: rect1.x1 < rect2.x2 && rect1.x2 > rect2.x1 && rect1.y1 < rect2.y2 && rect1.y2 > rect2.y1
  };
}
$(window).on("load scroll resize", function() {
  var viewportOffset = getViewportOffset($("#element"));
  $("#log").text("left: " + viewportOffset.left + ", top: " + viewportOffset.top + ", insideViewport: " + viewportOffset.insideViewport);
});
body { margin: 0; padding: 0; width: 1600px; height: 2048px; background-color: #CCCCCC; }
#element { width: 384px; height: 384px; margin-top: 1088px; margin-left: 768px; background-color: #99CCFF; }
#log { position: fixed; left: 0; top: 0; font: medium monospace; background-color: #EEE8AA; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<!-- scroll right and bottom to locate the blue square -->
<div id="element"></div>
<div id="log"></div>


2
ini berfungsi dengan baik untuk saya, tetapi saya menggunakan ini untuk menentukan apakah tooltip keluar dari batas, jadi saya telah memodifikasi untuk memasukkan nilai bawah dan kanan: jsfiddle.net/EY6Rk/21
Jordan

Bagaimanapun, ini adalah jawaban yang benar-benar benar. Semua yang lain memiliki masalah tergantung pada konfigurasi delta mouse / scroll, browser, lokasi obj, dll.
Pedro Ferreira

2

Berikut adalah fungsi yang menghitung posisi elemen saat ini dalam viewport:

/**
 * Calculates the position of a given element within the viewport
 *
 * @param {string} obj jQuery object of the dom element to be monitored
 * @return {array} An array containing both X and Y positions as a number
 * ranging from 0 (under/right of viewport) to 1 (above/left of viewport)
 */
function visibility(obj) {
    var winw = jQuery(window).width(), winh = jQuery(window).height(),
        elw = obj.width(), elh = obj.height(),
        o = obj[0].getBoundingClientRect(),
        x1 = o.left - winw, x2 = o.left + elw,
        y1 = o.top - winh, y2 = o.top + elh;

    return [
        Math.max(0, Math.min((0 - x1) / (x2 - x1), 1)),
        Math.max(0, Math.min((0 - y1) / (y2 - y1), 1))
    ];
}

Nilai yang dikembalikan dihitung seperti ini:

Pemakaian:

visibility($('#example'));  // returns [0.3742887830933581, 0.6103752759381899]

Demo:

function visibility(obj) {var winw = jQuery(window).width(),winh = jQuery(window).height(),elw = obj.width(),
    elh = obj.height(), o = obj[0].getBoundingClientRect(),x1 = o.left - winw, x2 = o.left + elw, y1 = o.top - winh, y2 = o.top + elh; return [Math.max(0, Math.min((0 - x1) / (x2 - x1), 1)),Math.max(0, Math.min((0 - y1) / (y2 - y1), 1))];
}
setInterval(function() {
  res = visibility($('#block'));
  $('#x').text(Math.round(res[0] * 100) + '%');
  $('#y').text(Math.round(res[1] * 100) + '%');
}, 100);
#block { width: 100px; height: 100px; border: 1px solid red; background: yellow; top: 50%; left: 50%; position: relative;
} #container { background: #EFF0F1; height: 950px; width: 1800px; margin-top: -40%; margin-left: -40%; overflow: scroll; position: relative;
} #res { position: fixed; top: 0; z-index: 2; font-family: Verdana; background: #c0c0c0; line-height: .1em; padding: 0 .5em; font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="res">
  <p>X: <span id="x"></span></p>
  <p>Y: <span id="y"></span></p>
</div>
<div id="container"><div id="block"></div></div>


0

Saya menemukan bahwa jawaban cballou tidak lagi berfungsi di Firefox mulai Januari 2014. Secara khusus, if (self.pageYOffset)tidak terpicu jika klien telah menggulir ke kanan, tetapi tidak ke bawah - karena 0ini adalah nomor palsu. Ini tidak terdeteksi untuk sementara waktu karena Firefox mendukung document.body.scrollLeft/ Top, tetapi ini tidak lagi berfungsi untuk saya (di Firefox 26.0).

Inilah solusi saya yang dimodifikasi:

var getPageScroll = function(document_el, window_el) {
  var xScroll = 0, yScroll = 0;
  if (window_el.pageYOffset !== undefined) {
    yScroll = window_el.pageYOffset;
    xScroll = window_el.pageXOffset;
  } else if (document_el.documentElement !== undefined && document_el.documentElement.scrollTop) {
    yScroll = document_el.documentElement.scrollTop;
    xScroll = document_el.documentElement.scrollLeft;
  } else if (document_el.body !== undefined) {// all other Explorers
    yScroll = document_el.body.scrollTop;
    xScroll = document_el.body.scrollLeft;
  }
  return [xScroll,yScroll];
};

Diuji dan berfungsi di FF26, Chrome 31, IE11. Hampir pasti bekerja pada versi yang lebih lama dari semuanya.

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.