ดึงตำแหน่ง (X, Y) ขององค์ประกอบ HTML ที่สัมพันธ์กับหน้าต่างเบราว์เซอร์


1567

ฉันต้องการทราบวิธีรับตำแหน่ง X และ Y ขององค์ประกอบ HTML เช่นimgและdivใน JavaScript ที่สัมพันธ์กับหน้าต่างเบราว์เซอร์


134
สัมพันธ์กับอะไร
AnthonyWJones

4
ฉันใช้โค้ด 7 บรรทัดเหล่านี้ซึ่งทำงานในเบราว์เซอร์ทั้งหมดที่มีความคลาดเคลื่อนใน ie5,6,7 (ฉันจำไม่ได้ว่ามี proboem ใด ๆ ... อาจเป็นประเภท doc) ... quirksmode.org/js/findpos htmlและฉันใช้มันมานานหลายปีแล้ว อาจมีสักคนที่สามารถชี้จุดบกพร่องได้ถ้ามี
Jayapal Chandran

98
@AththonyWJones ฉันคิดว่าคุณต้องการความสุขแบบอลังการ แต่เห็นได้ชัดเนื่องจากมันไม่ได้ระบุเสมอว่า OP หมายถึงกรณีทั่วไปมากที่สุดหรืออ้างอิงพิกัดหน้าต่างของเบราว์เซอร์
Motes

7
@ มลทินไม่ไม่ชัดเจน การอนุมานปล่อยทิ้งความเป็นส่วนตัวและสัจพจน์เท็จเอาไว้ มันสามารถสัมพันธ์กับ viewport หรือด้านบนของหน้า (aka document.documentElement)
Andre Figueiredo

15
การกำหนดค่าเริ่มต้นให้กับทั่วไปส่วนใหญ่ไม่ได้อนุมานมันเป็นความก้าวหน้าแบบลอจิคัลดังนั้นจะสัมพันธ์กับหน้าต่างหรือ "บนสุดของหน้า" อาจเป็นคำที่คุณใส่ไว้ ในทฤษฎีเกมมันถูกดัดแปลงเป็นแนวคิดที่เรียกว่า Schelling point ให้แน่ใจว่าได้ระบุเมื่อคุณไม่ได้หมายถึงกรณีทั่วไปมากที่สุด
Motes

คำตอบ:


1795

วิธีการที่ถูกต้องคือการใช้element.getBoundingClientRect():

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

Internet Explorer ได้ให้การสนับสนุนนี้ตั้งแต่ตราบใดที่คุณมีแนวโน้มที่จะดูแลเกี่ยวกับและมันเป็นมาตรฐานในที่สุดCSSOM ชม เบราว์เซอร์อื่น ๆ ทั้งหมดที่นำมาใช้เป็นเวลานานแล้ว

เบราว์เซอร์บางตัวยังส่งคืนคุณสมบัติความสูงและความกว้างแม้ว่านี่จะไม่ใช่มาตรฐาน หากคุณกังวลเกี่ยวกับความเข้ากันได้ของเบราว์เซอร์รุ่นเก่าให้ตรวจสอบการแก้ไขของคำตอบนี้เพื่อการใช้งานที่ลดประสิทธิภาพ

ค่าที่ส่งคืนโดยelement.getBoundingClientRect()สัมพันธ์กับวิวพอร์ต หากคุณต้องการมันสัมพันธ์กับองค์ประกอบอื่นเพียงแค่ลบหนึ่งสี่เหลี่ยมจากอื่น ๆ :

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

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

143
บทความที่น่าสนใจมากในพิกัด JS บทความนี้มีการกล่าวถึงไม่มีที่ไหนเลยดังนั้นมันควร ..
e2-e4

6
ใช้งานไม่ได้ ... เป็น 8 พิกเซลในแนวตั้งและแนวนอนเนื่องจากระยะขอบ 8px ในร่างกาย วิธีการแก้ปัญหาของ Meouw ทำงานได้อย่างสมบูรณ์แบบ หากคุณต้องการฉันมีการทดสอบขนาดเล็กเพื่อแสดงให้เห็นถึงปัญหา
CpnCrunch

3
ที่จริงฉันคิดว่ามันขึ้นอยู่กับสิ่งที่คุณต้องการรับ coords ของ คำถามนี้ค่อนข้างคลุมเครือ คำตอบของคุณถูกต้องหากคุณต้องการให้ coords สัมพันธ์กับวิวพอร์ตหรือสัมพันธ์กับองค์ประกอบของร่างกาย แต่นั่นไม่ได้ช่วยคุณในกรณีที่คุณต้องการตำแหน่งที่แน่นอนขององค์ประกอบ (ซึ่งฉันคิดว่าเป็นสิ่งที่คนส่วนใหญ่ต้องการ) . คำตอบของ meouw ไม่ได้ให้สิ่งนั้นกับคุณเว้นแต่คุณจะลบบิต '-scrollLeft' และ '-scrollTop'
CpnCrunch

4
@CpnCrunch: ฉันเห็นสิ่งที่คุณหมายถึงตอนนี้; ฉันไม่รู้ว่าคุณกำลังอ้างอิงถึงคำตอบสุดท้ายของฉัน เมื่อร่างกายมีระยะขอบกล่องขอบเขตของมันจะถูกชดเชยด้วยจำนวนพิกเซลในแต่ละด้านที่เกี่ยวข้อง ในกรณีนี้คุณจะต้องลบรูปแบบระยะขอบที่คำนวณได้จากพิกัดร่างกาย มันน่าสังเกตว่าการตั้งค่าใหม่ CSS ลบอัตรากำไรจาก<body>แม้ว่า
Andy E

3
element.getBoundingClientRect().topไม่ส่งกลับค่าออฟเซ็ตยอดนิยมที่ถูกต้องในกรณีของposition: fixedองค์ประกอบใน iOS (iPhone 8) หากมีองค์ประกอบอินพุตที่มุ่งเน้นและคีย์บอร์ดเสมือนเลื่อนขึ้น ตัวอย่างเช่นหากออฟเซ็ตจริงจากด้านบนของหน้าต่างคือ 200px มันจะรายงานว่าเป็น 420px โดยที่ 220px คือความสูงของคีย์บอร์ดเสมือน หากคุณตั้งค่าองค์ประกอบposition: absoluteและวางตำแหน่งไว้ที่ตำแหน่งเดียวกับที่กำหนดไว้คุณจะได้ 200px ที่ถูกต้อง ฉันไม่รู้วิธีแก้ปัญหานี้
Jānis Elmeris

327

ไลบรารี่มีความยาวพอเหมาะเพื่อให้ได้ออฟเซ็ตที่แม่นยำสำหรับองค์ประกอบ
นี่เป็นฟังก์ชั่นง่าย ๆ ที่ทำงานในทุกสถานการณ์ที่ฉันได้ลอง

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
change: el = el.parentNode เป็น: el = el.offsetParent; และดูเหมือนจะใช้งาน iframes ที่ซ้อนกันได้ในตอนนี้ ... ฉันคิดว่านั่นเป็นสิ่งที่คุณต้องการ
Adam

4
วิธีนี้ไม่สมบูรณ์ ลองวางเส้นหนาไว้บนกองแล้วทำสองสามครั้ง ใน Firefox ทุกรุ่น (2-5) มันจะถูกปิดโดยความกว้างของเส้นขอบ (แม้ในโหมดการปฏิบัติตามมาตรฐาน)
ck_

3
ไม่มีวิธีทำความสะอาดที่ดีกว่าเช่นฟังก์ชั่น el.totalOffsetLeft และ totalOffsetTop ใช่ไหม jQuery มีตัวเลือกนี้ แต่น่าเสียดายที่ไม่มีวิธีการอย่างเป็นทางการสำหรับเรื่องนี้เราต้องรอ DOM4 หรือไม่ ฉันกำลังสร้างแอปพลิเคชันผ้าใบ HTML5 ดังนั้นฉันคิดว่าทางออกที่ดีที่สุดคือการใส่องค์ประกอบผ้าใบใน iframe เพื่อให้ฉันสามารถรับพิกัดจริงด้วย clientX และ clientY เมื่อเราคลิกที่องค์ประกอบ
baptx

1
@Adam และ meouw คุณกำลังบอกว่าการบล็อกโค้ดครั้งแรกผิดหรือเปล่า? ถ้าอย่างนั้นทำไมไม่ลบมันออกไปเลยล่ะ? (คำถามนี้ได้เห็นผู้ชมบางส่วนในช่วงหลายปีที่ผ่านมาอาจจะดีที่จะลบสิ่งต่าง ๆ หากพวกเขาผิด?)
Arjan

2
@baptx: ฉันรู้ว่านี่เป็นคำตอบที่ล่าช้า แต่ฉันไม่เห็นความคิดเห็นของคุณเร็วกว่านี้ ดูคำตอบของฉันด้านล่างสำหรับทางเลือกที่เป็นไปตามมาตรฐานมากกว่า - คุณไม่จำเป็นต้องใช้รหัสสำรองเนื่องจากเบราว์เซอร์รองรับเป็นเวลานาน
Andy E

242

สิ่งนี้ใช้ได้สำหรับฉัน (แก้ไขจากคำตอบที่โหวตสูงสุด):

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

ใช้สิ่งนี้เราสามารถโทร

getOffset(element).left

หรือ

getOffset(element).top

1
วิธีแก้ปัญหานี้ใช้ได้เฉพาะกับฉันเท่านั้นเมื่อองค์ประกอบถูกวางไว้divที่ศูนย์กลางโดยใช้ css top:50%, left:50%; transform:translate(-50%, -50%);... ยอดเยี่ยม เห็นด้วยเล็กน้อย @ScottBiggs คำถาม ตรรกะคือการแสวงหาความเรียบง่าย
nelek

3
อาจไม่ใช่ผู้ชนะที่ทำให้เกิดปัญหาเช่นเดียวกับโซลูชั่นของ Andy E แต่ไม่ได้ทำงานในเบราว์เซอร์เก่า <IE9
niltoid

getBoundingClientRect ทำให้เกิดการทาสีซ้ำขนาดใหญ่ในชาร์ตการแสดงของฉัน
frumbert

1
คุณควรที่จะกำหนดrectใช้var, หรือlet มิฉะนั้นจะถูกกำหนดให้เป็นconst window.rect
hev1

2
left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2)จะกลับตำแหน่งกลางขององค์ประกอบ
abhishake

97

หากคุณต้องการให้ทำเฉพาะใน javascript ต่อไปนี้เป็นหนึ่งใน liners ที่ ใช้getBoundingClientRect()

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

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

บรรทัดแรกจะกลับมาoffsetTopว่า Y ที่สัมพันธ์กับเอกสาร บรรทัดที่สองจะกลับมาoffsetLeftบอกว่า X สัมพันธ์กับเอกสาร

getBoundingClientRect() เป็นฟังก์ชั่นจาวาสคริปต์ที่ส่งคืนตำแหน่งขององค์ประกอบที่สัมพันธ์กับวิวพอร์ตของหน้าต่าง


2
นี่ควรเป็นทางออก ไม่มีรหัส bs ยาวตรงนี้มันเข้าใจได้และแม่นยำมาก!
E Alexis MT

2
เกิดอะไรขึ้นถ้าองค์ประกอบอยู่ในองค์ประกอบที่เลื่อนได้? วิธีเดียวที่จะทำคือการรวม offsetTop ขององค์ประกอบผู้ปกครองทั้งหมดเช่นเดียวกับคำตอบอื่น ๆ ที่แนะนำ
Dmitri Pisarev

ในฐานะรัฐ Dmitri นี่เป็นสิ่งที่ไม่ถูกต้องและมีข้อบกพร่องมาก ไม่ควรมีการโหวตมากเท่าที่ควร มันจะอยู่ในองค์ประกอบที่เลื่อนได้เช่นกันเพราะเอกสารนั้นสามารถเลื่อนได้ กล่าวอีกนัยหนึ่งยกเว้นว่าเอกสารทั้งหมดเหมาะกับวิวพอร์ตมันจะไม่ถูกต้องเสมอหากผู้ใช้เลื่อนหน้าต่างหลักเลย
เลฟ

46

องค์ประกอบ HTML ในเบราว์เซอร์ส่วนใหญ่จะมี: -

offsetLeft
offsetTop

ระบุตำแหน่งขององค์ประกอบเหล่านี้เทียบกับพาเรนต์ที่ใกล้ที่สุดที่มีโครงร่าง พาเรนต์นี้มักจะสามารถเข้าถึงได้ถ้าคุณสมบัติ offsetParent

IE และ FF3 มี

clientLeft
clientTop

คุณสมบัติเหล่านี้พบได้ทั่วไปน้อยกว่าพวกเขาระบุตำแหน่งองค์ประกอบที่มีพื้นที่ไคลเอนต์ของผู้ปกครอง (พื้นที่เบาะเป็นส่วนหนึ่งของพื้นที่ลูกค้า แต่ชายแดนและขอบไม่ได้)


36

หากหน้ามี - อย่างน้อย - "DIV" ใด ๆ ฟังก์ชั่นที่กำหนดโดย meouw จะโยนค่า "Y" เกินขีด จำกัด หน้าปัจจุบัน เพื่อค้นหาตำแหน่งที่แน่นอนคุณต้องจัดการทั้ง offsetParent's และ parentNode's

ลองรหัสที่ระบุด้านล่าง (มีการตรวจสอบสำหรับ 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
เช่นเดียวกับโซลูชันอื่น ๆ แม้จะมี FF 24.4 โค้ดด้านบนไม่ทำงานเมื่อความกว้างของเส้นขอบมีอยู่เป็นส่วนหนึ่งของเค้าโครงการวางตำแหน่ง
ปล้น

คุณเป็นผู้ช่วยชีวิต ฉันกำลังทำงานกับข้อบกพร่อง GWT ที่ลึกล้ำในตอนนี้และใช้วิธีนี้ในการแก้ปัญหาทั้งหมดของฉัน !!
Will Byrne

35

คุณสามารถเพิ่มคุณสมบัติสองรายการElement.prototypeเพื่อรับส่วนบน / ซ้ายขององค์ประกอบใด ๆ

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

สิ่งนี้เรียกว่า:

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

นี่คือตัวอย่างการเปรียบเทียบผลลัพธ์กับ jQuery's offset().topและ.left: http://jsfiddle.net/ThinkingStiff/3G7EZ/


14
แก้ไขElement.prototypeโดยทั่วไปถือว่าเป็นความคิดที่ดี มันนำไปสู่การรักษารหัสยากมาก นอกจากนี้รหัสนี้ไม่บัญชีสำหรับการเลื่อน
Jared Forsyth

23

เพื่อดึงตำแหน่งที่สัมพันธ์กับหน้าอย่างมีประสิทธิภาพและไม่ต้องใช้ฟังก์ชันเรียกซ้ำ: (รวมถึง 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;

การรวม scrollTop / scrollLeft ที่สำคัญทั้งหมดทำให้คำตอบนี้ถูกต้องมากขึ้น
scunliffe

19

วิธีการเกี่ยวกับสิ่งนี้โดยผ่าน ID ขององค์ประกอบและมันจะกลับไปทางซ้ายหรือด้านบนเรายังสามารถรวมพวกเขา:

1) ค้นหาทางซ้าย

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

2) ค้นหาด้านบน

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

หรือ3) ค้นหาซ้ายและบนสุดพร้อมกัน

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

คุณอาจได้รับบริการที่ดีขึ้นโดยใช้เฟรมเวิร์ก JavaScript ที่มีฟังก์ชันเพื่อส่งคืนข้อมูลดังกล่าว (และอื่น ๆ อีกมากมาย!) ในแบบที่ไม่เกี่ยวข้องกับเบราว์เซอร์ นี่คือบางส่วน:

ด้วยเฟรมเวิร์กเหล่านี้คุณสามารถทำสิ่ง $('id-of-img').top ต่อไปนี้เพื่อรับพิกัด y-pixel ของภาพ


2
@Costa พวกเขาทุกคนใช้ฟังก์ชั่นประเภทต่าง ๆ แม้ว่านี่จะเป็นฟิลด์ที่กำลังพัฒนา รหัสส่วนใหญ่เกี่ยวข้องกับ "การแก้ไข" สิ่งที่ผิดพลาด คุณควรดูรหัสต้นฉบับจริง ๆ
Shalom Craimer

18
@scraimer: jQuery และ YUI ไม่ใช่กรอบ !!! นี่คือห้องสมุด ! มันไม่เหมือนกัน อ้างถึงjquery.com : "jQuery เป็นไลบรารี JavaScript ที่รวดเร็วขนาดเล็กและมีคุณลักษณะหลากหลาย" อ้างyuilibrary.com (คุณสามารถดู "มายากล" คำใน URL เกินไป ... ): "ยูอิเป็นฟรีเปิดแหล่ง JavaScript และ CSS ห้องสมุดสำหรับการสร้างโปรแกรมเว็บแบบโต้ตอบมั่งคั่ง."
Sk8erPeter

29
ดังนั้นคุณควรใช้ห้องสมุดขนาดใหญ่สำหรับงานง่าย ๆ นี้หรือไม่? ไม่จำเป็น. ฉันเกลียดที่ทุกครั้งที่ฉันต้องการทำอะไรกับ js ฉันจะได้รับ jQuery solution jQuery ไม่ใช่ JS!
Opptatt Jobber

1
@OpptattJobber ฉันเห็นด้วย ... JQuery ไม่ใช่ javascript มันอาศัย Javascript แต่ภาษาการเขียนโปรแกรมที่แตกต่างกันโดยสิ้นเชิง
Nick Manning

5
@NickManning ไม่ใช่ภาษาใหม่เป็นเลเยอร์นามธรรมสำหรับ DOM ที่ไม่จำเป็นต้องเขียนความเข้ากันได้และประสิทธิภาพในการแก้ไขปัญหาโดยตรงในรหัสของคุณ หากคุณไม่ได้ใช้ jQuery คุณควรใช้เลเยอร์ abstraction บางชนิดต่อไป
ginman

15

jQuery .offset ()จะได้รับพิกัดปัจจุบันขององค์ประกอบแรกหรือตั้งค่าพิกัดของทุกองค์ประกอบในชุดขององค์ประกอบที่ตรงกันสัมพันธ์กับเอกสาร


ด้วยเหตุผลบางอย่างมันไม่ได้ให้ผลลัพธ์ที่เหมือนกันใน IE เมื่อเทียบกับเบราว์เซอร์อื่น ๆ ฉันคิดว่าใน IE จะให้ตำแหน่งเทียบกับหน้าต่างดังนั้นถ้าคุณเลื่อนคุณจะได้รับผลลัพธ์ที่แตกต่างใน IE เมื่อเทียบกับคนอื่น ๆ
Abdul Rahim Haddad

1
offset () สับสนโดยการซูมอย่างน้อยใน Android Chrome ดังนั้นจึงไม่มีประโยชน์ stackoverflow.com/a/11396681/1691517แสดงวิธีการซูมได้
Timo Kähkönen

10

ฉันได้รับคำตอบของ @ meouw เพิ่มใน clientLeft ที่อนุญาตสำหรับเส้นขอบแล้วสร้างสามเวอร์ชัน:

getAbsoluteOffsetFromBody - คล้ายกับ @ meouw's สิ่งนี้จะได้รับตำแหน่งสัมบูรณ์ที่เกี่ยวข้องกับเนื้อหาหรือองค์ประกอบ HTML ของเอกสาร (ขึ้นอยู่กับโหมด quirks)

getAbsoluteOffsetFromGivenElement - ส่งคืนตำแหน่งสัมบูรณ์ที่สัมพันธ์กับองค์ประกอบที่กำหนด (relativeEl) โปรดทราบว่าองค์ประกอบที่กำหนดจะต้องมีองค์ประกอบ el หรือสิ่งนี้จะทำงานเช่นเดียวกับ getAbsoluteOffsetFromBody สิ่งนี้มีประโยชน์หากคุณมีสององค์ประกอบที่มีอยู่ภายในองค์ประกอบ (ที่รู้จัก) องค์ประกอบอื่น (เป็นทางเลือกหลายโหนดขึ้นไปที่ทรีโหนด) และต้องการทำให้พวกเขาอยู่ในตำแหน่งเดียวกัน

getAbsoluteOffsetFromRelative - ส่งคืนตำแหน่งสัมบูรณ์ที่สัมพันธ์กับองค์ประกอบพาเรนต์แรกที่มีตำแหน่ง: สัมพัทธ์ สิ่งนี้คล้ายกับ getAbsoluteOffsetFromGivenElement ด้วยเหตุผลเดียวกัน แต่จะไปไกลเท่าองค์ประกอบแรกที่ตรงกัน

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

หากคุณยังคงประสบปัญหาโดยเฉพาะอย่างยิ่งการเลื่อนคุณสามารถลองดูที่http://www.greywyvern.com/?post=331 - ฉันสังเกตเห็นอย่างน้อยหนึ่งชิ้นของรหัสที่น่าสงสัยใน getStyle ซึ่งควรเป็นเบราว์เซอร์ที่ทำงานได้ดี แต่ยังไม่ได้ทดสอบที่เหลือเลย


ที่น่าสนใจ ... แต่บน Edge getAbsoluteOffsetFromBody (องค์ประกอบ) .top จะส่งกลับค่าที่แตกต่างขณะที่ฉันเลื่อนใน Chrome มันจะคงที่เมื่อฉันเลื่อน ดูเหมือนว่า Edge กำลังปรับตำแหน่งการเลื่อน เมื่อองค์ประกอบถูกเลื่อนออกไปจากมุมมองฉันได้รับค่าลบ
นอร์แมน

getAbsoluteOffsetFromRelative ทำให้วันของฉันฉันต้องทำซ้ำคำแนะนำเครื่องมือ bootstrap โดยไม่ต้องใช้ Javascript ของ bootstrap มันช่วยฉันได้มาก
Nolyurn

ในกรณีที่ถ้า Meouw เปลี่ยนชื่อผู้ใช้ลิงค์ของพวกเขาที่จะตอบ: stackoverflow.com/a/442474/3654837 (ฉันไม่ต้องการที่จะชนหัวข้อเก่านี้ดังนั้นใช่ฉันใส่ความคิดเห็น)
Mukyuu

8

หากใช้ jQuery ปลั๊กอินขนาดนั้นยอดเยี่ยมและช่วยให้คุณระบุสิ่งที่คุณต้องการได้อย่างแท้จริง

เช่น

ตำแหน่งสัมพัทธ์, ตำแหน่งสัมบูรณ์, ตำแหน่งสัมบูรณ์โดยไม่ต้องแพ็ดด ...

มันบอกต่อไปว่ามีหลายอย่างที่คุณสามารถทำได้

นอกจากนี้โบนัสของการใช้ jQuery ก็คือขนาดไฟล์ที่เบาและใช้งานง่ายคุณจะไม่กลับไปที่ JavaScript หลังจากนั้น


8

นี่คือรหัสที่ดีที่สุดที่ฉันสามารถสร้างได้ (ทำงานใน iframe เช่นกันซึ่งแตกต่างจาก jQuery's offset ()) ดูเหมือนว่า webkit มีพฤติกรรมที่แตกต่างกันเล็กน้อย

ตามความคิดเห็นของ 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 };
}

โปรดทราบว่าคุณจะต้องjQueryใช้$.browser.webkit; คุณจะต้องเล่นรอบกับที่จะทำเช่นเดียวกันกับที่บริสุทธิ์navigator.userAgent JavaScript
SP

2
$ .browser ไม่สามารถใช้ได้ใน jQuery เวอร์ชันล่าสุด
Gus

น่าเสียดายที่ถึงแม้จะมี FF 24.4 รหัสข้างต้นก็ใช้งานไม่ได้เมื่อมีความกว้างของเส้นขอบเป็นส่วนหนึ่งของเค้าโครงการวางตำแหน่ง
ปล้น

8

หากคุณใช้ jQuery นี่อาจเป็นวิธีง่ายๆ:

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

8

ความแตกต่างระหว่างเล็กและน้อย

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

ดูตัวอย่างพิกัด: http://javascript.info/tutorial/coordinates


น่าเสียดายที่นี่ใช้ไม่ได้กับองค์ประกอบทั้งหมดที่อาจพบในเอกสาร HTML ที่ทันสมัย แบบฝัง<svg>องค์ประกอบเช่นไม่ได้offsetLeft, offsetTopและoffsetParentคุณสมบัติ
Jonathan Eunice

เพียงแค่ใช้DIVหรือไม่IMG SVGดู "ดูพิกัดตัวอย่าง" หรือไม่ คุณสามารถค้นหาวัตถุ svg คือการเรียกใช้ตำแหน่งgetOffsetRectลองและทำงานของวัตถุ
KingRider

7

วิธีที่สะอาดที่ฉันได้พบเป็นรุ่นที่เรียบง่ายของเทคนิคที่ใช้โดยของ offsetjQuery คล้ายกับบางส่วนของคำตอบอื่น ๆ จะเริ่มต้นด้วยgetBoundingClientRect; จากนั้นใช้windowและdocumentElementเพื่อปรับสำหรับตำแหน่งการเลื่อนตลอดจนสิ่งต่าง ๆ เช่นระยะขอบบนbody(มักเป็นค่าเริ่มต้น)

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

ในขณะนี้มีแนวโน้มที่จะหายไปที่ด้านล่างของคำตอบมากมายโซลูชั่นชั้นนำที่นี่ไม่ทำงานสำหรับฉัน
เท่าที่ฉันจะบอกได้ว่าไม่มีคำตอบอื่นใดช่วย

สถานการณ์ :
ในหน้า HTML5 ฉันมีเมนูที่เป็นองค์ประกอบ nav ภายในส่วนหัว (ไม่ใช่ส่วนหัว แต่เป็นส่วนหัวในองค์ประกอบอื่น)
ฉันต้องการให้การนำทางติดไปด้านบนเมื่อผู้ใช้เลื่อนไปที่หน้า แต่ก่อนหน้านี้ส่วนหัวอยู่ในตำแหน่งที่แน่นอน (ดังนั้นฉันจึงสามารถวางทับสิ่งอื่นเล็กน้อย)
วิธีการแก้ปัญหาข้างต้นไม่เคยทำให้เกิดการเปลี่ยนแปลงเพราะ. offsetTop จะไม่เปลี่ยนแปลงเนื่องจากเป็นองค์ประกอบตำแหน่งที่แน่นอน นอกจากนี้คุณสมบัติ. scrollTop เป็นเพียงส่วนบนสุดขององค์ประกอบส่วนใหญ่ ... นั่นคือจะบอกว่า 0 และจะเป็น 0 เสมอ
การทดสอบใด ๆ ที่ฉันทำโดยใช้สองสิ่งนี้ (และเหมือนกันกับผลลัพธ์ของ getBoundingClientRect) จะไม่บอกฉันว่าแถบด้านบนสุดของแถบการนำทางที่เคยเลื่อนไปด้านบนของหน้าสามารถดูได้ (อีกครั้งตามที่รายงานในคอนโซล เกิดขึ้น)

วิธีการแก้ปัญหา
การแก้ปัญหาสำหรับผมที่ได้รับใช้

window.visualViewport.pageTop

ค่าของคุณสมบัติ pageTop สะท้อนถึงส่วนที่สามารถดูได้ของหน้าจอดังนั้นให้ฉันติดตามตำแหน่งที่องค์ประกอบอ้างอิงถึงขอบเขตของพื้นที่ที่สามารถดูได้

อาจไม่จำเป็นต้องพูดทุกครั้งที่ฉันต้องทำการเลื่อนฉันคาดว่าจะใช้โซลูชันนี้เพื่อตอบสนองการเคลื่อนไหวขององค์ประกอบที่ถูกเลื่อนโดยทางโปรแกรม
หวังว่ามันจะช่วยคนอื่น
หมายเหตุสำคัญ: สิ่งนี้ดูเหมือนว่าจะใช้งานได้ใน Chrome และ Opera ในขณะนี้ & ไม่ใช่ใน Firefox (6-2018) ... จนกระทั่ง Firefox รองรับ visualViewport ฉันไม่แนะนำให้ใช้วิธีนี้ (และฉันหวังว่าพวกเขาจะทำเร็ว ๆ นี้ ... มีความรู้สึกมากกว่าส่วนที่เหลือ)


อัปเดต:
โปรดทราบเกี่ยวกับโซลูชันนี้
ในขณะที่ฉันยังพบสิ่งที่ฉันค้นพบว่ามีค่ามากสำหรับสถานการณ์ที่ "... ตอบสนองโดยทางโปรแกรมต่อการเคลื่อนไหวขององค์ประกอบที่ถูกเลื่อน" ใช้ได้ ทางออกที่ดีกว่าสำหรับปัญหาที่ฉันมีคือการใช้ CSS เพื่อกำหนดตำแหน่ง: เหนียวในองค์ประกอบ ใช้เหนียวคุณสามารถมีความโดดองค์ประกอบที่ด้านบนโดยไม่ต้องใช้ JavaScript (หมายเหตุ: มีครั้งนี้จะไม่ทำงานอย่างมีประสิทธิภาพเป็นที่เปลี่ยนแปลงองค์ประกอบคงที่ แต่สำหรับการใช้งานมากที่สุดวิธีเหนียวมีแนวโน้มที่จะดีกว่า)

UPDATE01:
ดังนั้นผมจึงตระหนักว่า สำหรับหน้าอื่นฉันมีข้อกำหนดที่ฉันต้องการตรวจสอบตำแหน่งขององค์ประกอบในการตั้งค่าการเลื่อนที่ซับซ้อนอย่างอ่อนโยน (parallax plus องค์ประกอบที่เลื่อนผ่านมาเป็นส่วนหนึ่งของข้อความ) ฉันตระหนักในสถานการณ์ที่ว่าสิ่งต่อไปนี้ให้คุณค่าที่ฉันใช้เพื่อพิจารณาว่าจะทำอะไรเมื่อไหร่:

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

หวังว่าคำตอบนี้มีประโยชน์มากขึ้นในขณะนี้


นี่เป็นโอกาสที่จะอยู่ด้านบนสุดของคำตอบมากมายเมื่อคุณคลิกที่ใช้งานเพื่อเรียงลำดับคำตอบ
lucchi

@lucchi: ดีฉันคิดว่าฉันสามารถลบข้อความ @ ด้านบน ... แม้ว่าฉันเพิ่งเกิดขึ้นในนี้อีกครั้งและตระหนักว่าฉันได้พบทางออกที่ดีกว่าสำหรับปัญหาของตัวเอง ... แม้ว่าคำตอบของฉันเป็นมากกว่า บันทึกเท้าเพื่อคำตอบที่สมบูรณ์มากขึ้น ฉันสงสัยว่าความต้องการของฉันอาจไม่ใช่เรื่องธรรมดาที่คำตอบอื่น ๆ ใช้ไม่ได้สำหรับฉัน
MER

1
ฉันขอขอบคุณคำตอบของคุณต่อไปมันเป็นเพียงการบอกคุณว่าใครก็ตามที่เต็มใจจะทราบว่าคุณได้เขียนอะไรใหม่ ๆ คุณไม่ได้อยู่คนเดียว ....
lucchi

5

ฉันทำแบบนี้ดังนั้นจึงสามารถใช้งานร่วมกับเบราว์เซอร์รุ่นเก่าได้

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

ข้อมูลเพิ่มเติมเกี่ยวกับพิกัดใน JavaScript ที่นี่: http://javascript.info/tutorial/coordinates


4

ในการรับออฟเซ็ตรวมขององค์ประกอบคุณสามารถสรุปการหักล้างพาเรนต์ทั้งหมดซ้ำ:

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

ด้วยฟังก์ชั่นยูทิลิตี้นี้การชดเชยยอดรวมทั้งหมดขององค์ประกอบ dom คือ:

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

ขอบคุณThinkingStiffสำหรับคำตอบนี่เป็นอีกเวอร์ชั่นเท่านั้น


2

ฉันใช้โซลูชันของ Andy E เพื่อวาง bootstrap 2 modal ขึ้นอยู่กับลิงค์ในแถวตารางที่ผู้ใช้คลิก หน้านี้เป็นหน้า Tapestry 5 และจาวาสคริปต์ด้านล่างจะถูกนำเข้าในคลาสหน้าจาวา

javascript:

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

}

รหัสกิริยาของฉัน:

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

ลิงค์ในลูป:

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

และรหัส java ในระดับหน้า:

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

หวังว่านี่จะช่วยใครซักคนโพสต์นี้ช่วยได้


รหัส js นี้ช่วยฉันฉันมีแอป webforms เก่าโดยใช้ตัวยึดหลายตำแหน่งแทรกหน้า. aspx และฉันไม่สามารถหาวิธีที่จะผนวก appildChild () ของกล่องป๊อปอัปที่ฉันสร้างขึ้นเพื่อรับเข้าวิวพอร์ตเนื่องจากต้นไม้ DOM ทั้งหมดตี ด้วยตัวยึดหลายตำแหน่งและมาร์กอัปที่ไม่ถูกต้อง การใช้ body.getBoundingClientRect () จากนั้นใช้ตำแหน่งบนสุดในการวางตำแหน่งคือสิ่งที่ฉันต้องการ ขอบคุณ
แบรดมาร์ติน

1

หลังจากการวิจัยและทดสอบมากมายดูเหมือนว่าจะได้ผล

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

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


1

แค่คิดว่าฉันจะโยนมันออกไปที่นั่นเช่นกัน
ฉันไม่สามารถทดสอบได้ในเบราว์เซอร์รุ่นเก่า แต่ใช้งานได้กับ 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

เนื่องจากเบราว์เซอร์ที่แตกต่างกันมีการแสดงเส้นขอบการเว้นระยะขอบและอื่น ๆ ในวิธีที่ต่าง ฉันเขียนฟังก์ชันเล็กน้อยเพื่อดึงตำแหน่งด้านบนและด้านซ้ายขององค์ประกอบเฉพาะในทุกองค์ประกอบรากที่คุณต้องการในมิติที่แม่นยำ:

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

เพื่อดึงตำแหน่งที่เหลือคุณต้องกลับมา:

    return offsetRect.left - rootRect.left;

-1

รับตำแหน่ง div ในส่วนซ้ายและบน

  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 

ฉันลงคะแนนเพราะล่างและขวาไม่ใช่คุณสมบัติของ offset ()
MacroMan

@MacroMan ฉันเคารพการตัดสินใจของคุณ แต่ฉันได้อธิบายไปแล้วในข้อความว่า "ไม่ได้ทดสอบรหัสด้านล่างและขวา" นอกจากนี้ฉันจะอัปเดตโค้ดของฉันในไม่ช้า
Vishal

ฉันคิดว่ามันel.offsetTopและel.offsetLeft(OP ไม่ได้พูดอะไรเกี่ยวกับ jQuery ... ฉันไม่แน่ใจว่าทำไมคำตอบของคุณใช้ jQuery ทั้ง ... )
mesqueeb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.