Получить позицию / смещение элемента относительно родительского контейнера?

125

Я привык работать с jQuery. Однако в моем текущем проекте я использую zepto.js. Zepto не предоставляет такой position()метод, как jQuery. Zepto поставляется только с offset().

Любая идея, как я могу получить смещение контейнера относительно родителя с чистым js или с Zepto?

матовый
источник
4
Проголосовал против принятия ответа jQuery (который даже не помечен как jQuery!) При задании вопроса JavaScript.
Джон
@John вы заметили метку zepto?
Supersharp
@Supersharp Используйте этот JavaScriptтег, когда используете чистый JavaScript. Если вы используете фреймворк или библиотеку, вы не используете JavaScriptтег. Если тебя зовут Джефф, я бы не стал называть тебя Салли.
Джон
@John это мнение, но так устроено не так. Пожалуйста, прочтите руководство по тегам javascript. В этом случае его следует использовать вместе с тегом библиотеки.
Supersharp
@Supersharp Да ТАК такой некомпетентный; буквальное раздувание тегов. 😒︎
Джон

Ответы:

188

Предупреждение: jQuery, а не стандартный JavaScript

element.offsetLeftи element.offsetTopявляются чистыми свойствами javascript для поиска позиции элемента по отношению к нему 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
}

Это дает вам смещение дочернего элемента относительно его родителя, даже если родительский элемент не расположен.

jackwanders
источник
9
Позиционированные staticэлементы не являются родителями смещения.
Esailija
5
не используйте точки с запятой в фигурных скобках, используйте запятые
Лука Боррионе
1
как насчет фиксированной позиции? они зачет родителей?
Ayyash
1
Да, @ Ayya- position fixed также создает новый контекст смещения
mmmeff
9
@vsync jQuery не является королем ничего с тех пор, как ie9 достиг EOL в январе 2016 года, у нас есть стандартизированный выбор 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?
matt
2
p.offsetTop + p.parentNode.offsetTopВы можете обернуть это рекурсивным вызовом функции, как это было предложено несколько лет назад PPK . Вы можете изменить функцию, чтобы передать количество уровней, на которые нужно подняться, или использовать селектор для имитации $(selector).parents(parentSelector). Я бы предпочел это решение, потому что реализация zepto работает только с поддерживающими браузерами, getBoundingClientsRectчего нет на некоторых мобильных устройствах.
Torsten Walter
Я думал, что getBoundingClientsRectполучил широкую поддержку ... если кто-нибудь знает, какие мобильные телефоны не поддерживают его, мне было бы интересно (не могу найти ни одной таблицы поддержки для мобильных устройств).
Нобита
Вероятно, это наиболее правильный ответ, даже если он не отмечен. Мое личное решение изменилось $(this).offset().leftна this.offsetLeft.
adjwilli 06
Это также исправляет неправильное jQuery.position()поведение внутри 3D-преобразованного родителя, большое вам спасибо!
rassoh 03
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;

    }
}

Когда целью события не является элемент, для которого событие было зарегистрировано, он добавляет смещение родительского элемента к текущему смещению события, чтобы вычислить «абсолютное» значение смещения.

Согласно веб-API Mozilla: «Свойство HTMLElement.offsetLeft, доступное только для чтения, возвращает количество пикселей, на которое верхний левый угол текущего элемента смещен влево внутри узла HTMLElement.offsetParent ».

В основном это происходит, когда вы регистрируете событие для родителя, который содержит еще несколько дочерних liэлементов , например: кнопку с внутренним значком или текстовым диапазоном, элемент с внутренними промежутками. и т.д...

DeiDei
источник
А что насчет родителя родителя? А как насчет вложенных элементов, имеющих полосы прокрутки? А как насчет предыдущих элементов с отображением: нет? Истинное положение смещения документа практически невозможно вычислить, но механизм визуализации может это знать. Жаль, что простые свойства, такие как позиция документа, никогда не включались в DOM.
Дэвид Спектор
@DavidSpector word
1

пример

Итак, если бы у нас был дочерний элемент с идентификатором «child-element», и мы хотели бы получить его левую / верхнюю позицию относительно родительского элемента, скажем, для 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

$(this).offsetRelative("div.item-parent");
Иршад Хан
источник
Я не вижу объяснения прокрутки. Может случиться много странных случаев.
Дэвид Спектор
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>
Мойзес Конехо
источник
Вы тестировали все возможные случаи родительских узлов и свойств элементов? Учитывает ли это прокрутку и вложенную прокрутку?
Дэвид Спектор