รับตำแหน่ง / ออฟเซ็ตขององค์ประกอบที่สัมพันธ์กับคอนเทนเนอร์หลักหรือไม่


125

ฉันเคยทำงานกับ jQuery ในโครงการปัจจุบันของฉัน แต่ฉันใช้ zepto.js Zepto ไม่มีposition()วิธีการเหมือนที่ jQuery ทำ Zepto มาพร้อมกับoffset()ไฟล์.

มีความคิดอย่างไรที่ฉันจะเรียกค่าออฟเซ็ตของคอนเทนเนอร์ที่สัมพันธ์กับพาเรนต์ด้วย pure js หรือกับ Zepto


4
ลงคะแนนยอมรับคำตอบ jQuery (ซึ่งไม่ได้ระบุว่าเป็น jQuery!) เมื่อถามคำถาม JavaScript
John

@ จอห์นคุณสังเกตเห็นแท็ก zepto หรือไม่?
Supersharp

@Supersharp ใช้JavaScriptแท็กเมื่อคุณใช้ JavaScript บริสุทธิ์ หากคุณใช้เฟรมเวิร์กหรือไลบรารีคุณจะไม่ใช้JavaScriptแท็ก ถ้าคุณชื่อเจฟฟ์ฉันจะไม่เรียกคุณว่าแซลลี่
John

@ จอห์นมันเป็นความเห็น แต่มันไม่ใช่วิธีการทำงานของ SO โปรดอ่านคู่มือแท็กจาวาสคริปต์ ในกรณีนี้ควรใช้ร่วมกับแท็กไลบรารี
Supersharp

@Supersharp ใช่ดังนั้นไร้ความสามารถเช่นนั้น; อัตราเงินเฟ้อตามตัวอักษร 😒︎
John

คำตอบ:


188

คำเตือน: jQuery ไม่ใช่ JavaScript มาตรฐาน

element.offsetLeftและelement.offsetTopมีความบริสุทธิ์คุณสมบัติจาวาสคริปต์ในการหาตำแหน่งขององค์ประกอบที่เกี่ยวกับมันoffsetParent; เป็นองค์ประกอบหลักที่ใกล้ที่สุดโดยมีตำแหน่งrelativeหรือabsolute

หรือคุณสามารถใช้Zeptoเพื่อรับตำแหน่งขององค์ประกอบและแม่ของมันได้ตลอดเวลาและลบทั้งสอง:

var childPos = obj.offset();
var parentPos = obj.parent().offset();
var childOffset = {
    top: childPos.top - parentPos.top,
    left: childPos.left - parentPos.left
}

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


9
ตำแหน่งขององค์ประกอบstaticไม่ได้หักล้างผู้ปกครอง
Esailija

5
อย่าใช้อัฒภาคในวงเล็บปีกกาใช้ลูกน้ำ
Luca Borrione

1
ตำแหน่งคงที่ล่ะ พวกเขาชดเชยพ่อแม่?
Ayyash

1
ใช่ @ Ayya- ตำแหน่งคงที่ยังสร้างบริบทออฟเซ็ตใหม่
mmmeff

9
@vsync jQuery ไม่ใช่ราชาของสิ่งใดเลยตั้งแต่ ie9 ถึง EOL ในเดือนมกราคม 2559 เรามีการเลือก DOM ที่เป็นมาตรฐานในเบราว์เซอร์หลักทั้งหมดเรียนรู้ js ที่บริสุทธิ์ นอกจากนี้ความคิดเห็นเกี่ยวกับกรอบการทำงานที่จะใช้ไม่มีที่วางบน SO เนื่องจากขึ้นอยู่กับโครงการและแพลตฟอร์มเป้าหมายเป็นอย่างมาก
Hans Koch

72

ใน js บริสุทธิ์เพียงแค่ใช้offsetLeftและoffsetTopคุณสมบัติ
ตัวอย่างซอ: http://jsfiddle.net/WKZ8P/

var elm = document.querySelector('span');
console.log(elm.offsetLeft, elm.offsetTop);
p   { position:relative; left:10px; top:85px; border:1px solid blue; }
span{ position:relative; left:30px; top:35px; border:1px solid red; }
<p>
    <span>paragraph</span>
</p>


ขอบคุณ. นอกจากนี้ยังมีวิธีในการหักล้างกับองค์ประกอบ parent.parent หรือไม่
แมตต์

2
p.offsetTop + p.parentNode.offsetTopคุณสามารถรวมสิ่งนี้ไว้ในการเรียกฟังก์ชันแบบเรียกซ้ำได้เหมือนกับที่ PPK เสนอเมื่อไม่กี่ปีที่ผ่านมา $(selector).parents(parentSelector)คุณสามารถปรับเปลี่ยนฟังก์ชั่นในการส่งผ่านข้อมูลจำนวนของระดับที่จะไปขึ้นหรือใช้ตัวเลือกที่จะเลียนแบบ ฉันต้องการโซลูชันนี้เนื่องจากการใช้งาน zepto ใช้งานได้เฉพาะกับเบราว์เซอร์ที่รองรับgetBoundingClientsRect- ซึ่งโทรศัพท์มือถือบางรุ่นไม่ทำ
Torsten Walter

ฉันคิดว่าgetBoundingClientsRectได้รับการสนับสนุนอย่างกว้างขวาง ... ถ้าใครรู้ว่าโทรศัพท์มือถือรุ่นใดไม่รองรับฉันจะสนใจ (ไม่พบตารางสนับสนุนสำหรับโทรศัพท์มือถือเกี่ยวกับเรื่องนี้)
โนบิตะ

นี่อาจเป็นคำตอบที่ถูกต้องที่สุดแม้ว่าจะไม่ได้ทำเครื่องหมายไว้ก็ตาม วิธีการแก้ปัญหาส่วนตัวของฉันก็เปลี่ยนไป$(this).offset().left this.offsetLeft
adjwilli

นอกจากนี้ยังแก้ไขjQuery.position()พฤติกรรมที่ไม่ถูกต้องภายในผู้ปกครองที่เปลี่ยนรูปแบบ 3 มิติด้วยขอบคุณมาก!
rassoh

6

ฉันทำแบบนี้ใน Internet Explorer

function getWindowRelativeOffset(parentWindow, elem) {
    var offset = {
        left : 0,
        top : 0
    };
    // relative to the target field's document
    offset.left = elem.getBoundingClientRect().left;
    offset.top = elem.getBoundingClientRect().top;
    // now we will calculate according to the current document, this current
    // document might be same as the document of target field or it may be
    // parent of the document of the target field
    var childWindow = elem.document.frames.window;
    while (childWindow != parentWindow) {
        offset.left = offset.left + childWindow.frameElement.getBoundingClientRect().left;
        offset.top = offset.top + childWindow.frameElement.getBoundingClientRect().top;
        childWindow = childWindow.parent;
    }

    return offset;
};

=================== เรียกแบบนี้ก็ได้

getWindowRelativeOffset(top, inputElement);

ฉันมุ่งเน้นไปที่ IE ตามโฟกัสของฉันเท่านั้น แต่สิ่งที่คล้ายกันนี้สามารถทำได้สำหรับเบราว์เซอร์อื่น


1

เพิ่มออฟเซ็ตของเหตุการณ์ให้กับออฟเซ็ตองค์ประกอบหลักเพื่อให้ได้ตำแหน่งออฟเซ็ตสัมบูรณ์ของเหตุการณ์

ตัวอย่าง :

HTMLElement.addEventListener('mousedown',function(e){

    var offsetX = e.offsetX;
    var offsetY = e.offsetY;

    if( e.target != this ){ // 'this' is our HTMLElement

        offsetX = e.target.offsetLeft + e.offsetX;
        offsetY = e.target.offsetTop + e.offsetY;

    }
}

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

ตาม Mozilla Web API: "คุณสมบัติHTMLElement.offsetLeft แบบอ่านอย่างเดียวจะส่งคืนจำนวนพิกเซลที่มุมบนซ้ายขององค์ประกอบปัจจุบันหักล้างไปทางซ้ายภายในโหนด HTMLElement.offsetParent "

สิ่งนี้ส่วนใหญ่เกิดขึ้นเมื่อคุณลงทะเบียนเหตุการณ์บนพาเรนต์ที่มีลูกเพิ่มอีกหลายตัวเช่นปุ่มที่มีไอคอนด้านในหรือช่วงข้อความliองค์ประกอบที่มีช่วงภายใน ฯลฯ ...


แล้วพ่อแม่ของผู้ปกครองล่ะ? องค์ประกอบที่ซ้อนกันมีแถบเลื่อนล่ะ? องค์ประกอบก่อนหน้านี้ที่มีการแสดงผล: ไม่มี? ตำแหน่งออฟเซ็ตเอกสารที่แท้จริงแทบจะเป็นไปไม่ได้ที่จะคำนวณ แต่เอ็นจินการเรนเดอร์สามารถรู้ได้ คุณสมบัติง่ายๆที่แย่เกินไปเช่นตำแหน่งเอกสารไม่เคยรวมอยู่ใน DOM
David Spector

@DavidSpector word

1

ตัวอย่าง

ดังนั้นหากเรามีองค์ประกอบลูกที่มี id ของ "องค์ประกอบลูก" และเราต้องการให้มันอยู่ในตำแหน่งซ้าย / บนสุดเทียบกับองค์ประกอบหลักให้พูดว่า div ที่มีคลาสเป็น "item-parent" เราจะ ใช้รหัสนี้

var position = $("#child-element").offsetRelative("div.item-parent");
alert('left: '+position.left+', top: '+position.top);

ปลั๊กอินสุดท้ายสำหรับปลั๊กอินจริง (มีบันทึกย่อบางส่วนที่อธิบายถึงสิ่งที่เกิดขึ้น):

// offsetRelative (or, if you prefer, positionRelative)
(function($){
    $.fn.offsetRelative = function(top){
        var $this = $(this);
        var $parent = $this.offsetParent();
        var offset = $this.position();
        if(!top) return offset; // Didn't pass a 'top' element 
        else if($parent.get(0).tagName == "BODY") return offset; // Reached top of document
        else if($(top,$parent).length) return offset; // Parent element contains the 'top' element we want the offset to be relative to 
        else if($parent[0] == $(top)[0]) return offset; // Reached the 'top' element we want the offset to be relative to 
        else { // Get parent's relative offset
            var parent_offset = $parent.offsetRelative(top);
            offset.top += parent_offset.top;
            offset.left += parent_offset.left;
            return offset;
        }
    };
    $.fn.positionRelative = function(top){
        return $(this).offsetRelative(top);
    };
}(jQuery));

หมายเหตุ:คุณสามารถใช้สิ่งนี้บน mouseClick หรือ mouseover Event

$(this).offsetRelative("div.item-parent");

ฉันไม่เห็นการเลื่อนคิดเป็นสัดส่วน มีกรณีแปลก ๆ มากมายที่สามารถเกิดขึ้นได้
David Spector

1

ฉันได้รับโซลูชันอื่น ลบค่าคุณสมบัติหลักจากค่าคุณสมบัติลูก

$('child-div').offset().top - $('parent-div').offset().top;

0

แน่นอนว่าเป็นเรื่องง่ายด้วย JS ที่บริสุทธิ์เพียงแค่ทำสิ่งนี้ทำงานกับแผง HTML 5 แบบคงที่และเคลื่อนไหวได้เช่นกันฉันสร้างและลองใช้รหัสนี้และใช้งานได้กับเบราว์เซอร์ใด ๆ (รวม IE 8):

<script type="text/javascript">
    function fGetCSSProperty(s, e) {
        try { return s.currentStyle ? s.currentStyle[e] : window.getComputedStyle(s)[e]; }
        catch (x) { return null; } 
    }
    function fGetOffSetParent(s) {
        var a = s.offsetParent || document.body;

        while (a && a.tagName && a != document.body && fGetCSSProperty(a, 'position') == 'static')
            a = a.offsetParent;
        return a;
    }
    function GetPosition(s) {
        var b = fGetOffSetParent(s);

        return { Left: (b.offsetLeft + s.offsetLeft), Top: (b.offsetTop + s.offsetTop) };
    }    
</script>

คุณทดสอบกับโหนดหลักและคุณสมบัติองค์ประกอบที่เป็นไปได้ทั้งหมดหรือไม่ บัญชีนี้ใช้สำหรับการเลื่อนและการเลื่อนซ้อนหรือไม่
David Spector
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.