Uzyskaj widoczną wysokość elementu div za pomocą jQuery

86

Muszę pobrać widoczną wysokość elementu div w przewijalnym obszarze. Uważam się za całkiem przyzwoitego z jQuery, ale to kompletnie mnie wyrzuca.

Powiedzmy, że mam czerwony element DIV w czarnym opakowaniu:

Na powyższej ilustracji funkcja jQuery zwróciłaby 248, widoczną część elementu div.

Gdy użytkownik przewinie się poza górę elementu div, jak na powyższej ilustracji, zgłosi 296.

Teraz, gdy użytkownik przewinie się poza div, ponownie zgłosi 248.

Oczywiście moje liczby nie będą tak spójne i jasne, jak w tym demo, albo po prostu zakodowałbym te liczby.

Mam trochę teorii:

  • Uzyskaj wysokość okna
  • Uzyskaj wysokość div
  • Uzyskaj początkowe przesunięcie elementu DIV z góry okna
  • Uzyskaj przesunięcie, gdy użytkownik przewija.
    • Jeśli przesunięcie jest dodatnie, oznacza to, że góra elementu div jest nadal widoczna.
    • jeśli jest ujemna, górna część elementu div została przyćmiona przez okno. W tym momencie element div może zajmować całą wysokość okna lub może być wyświetlany spód elementu div
    • Jeśli wyświetla się dół elementu div, sprawdź odstęp między nim a dołem okna.

Wydaje się to całkiem proste, ale po prostu nie mogę tego obejść. Jutro rano zjem kolejną porcję; Pomyślałem, że niektórzy z was, geniusze, mogliby pomóc.

Dzięki!

AKTUALIZACJA: Sam to wymyśliłem, ale wygląda na to, że jedna z poniższych odpowiedzi jest bardziej elegancka, więc zamiast tego użyję tego. Dla ciekawskich oto co wymyśliłem:

$(document).ready(function() {
    var windowHeight = $(window).height();
    var overviewHeight = $("#overview").height();
    var overviewStaticTop = $("#overview").offset().top;
    var overviewScrollTop = overviewStaticTop - $(window).scrollTop();
    var overviewStaticBottom = overviewStaticTop + $("#overview").height();
    var overviewScrollBottom = windowHeight - (overviewStaticBottom - $(window).scrollTop());
    var visibleArea;
    if ((overviewHeight + overviewScrollTop) < windowHeight) {
        // alert("bottom is showing!");
        visibleArea = windowHeight - overviewScrollBottom;
        // alert(visibleArea);
    } else {
        if (overviewScrollTop < 0) {
            // alert("is full height");
            visibleArea = windowHeight;
            // alert(visibleArea);
        } else {
            // alert("top is showing");
            visibleArea = windowHeight - overviewScrollTop;
            // alert(visibleArea);
        }
    }
});
JacobTheDev
źródło
Przyjrzałbym się jednostkom VH. 1vh = 1/100 wysokości rzutni. Prawdopodobnie znajdziesz na to rozwiązanie. Znajdź wysokość rzutni, wysokość elementu, pozycję elementów oraz pozycję przewijania i odpowiednio oblicz.
davidcondrey
Zakładając, że na wewnętrznym DIV jest margines, powiedz „10px” dookoła. Wykrywałbym wysokość przewijania, aby sprawdzić, czy przekroczyła wartość „10”, a następnie po prostu uzyskałbym wysokość elementu nadrzędnego, odejmując ją na podstawie wysokości przewijania.
Jeśli wszystko inne zawiedzie, właśnie natknąłem się na ten skrypt, który wygląda na to, że może służyć twojemu celowi
davidcondrey.

Odpowiedzi:

57

Oto szybka i brudna koncepcja. Zasadniczo porównuje offset().topelement do górnej części okna i offset().top + height()do dołu okna:

function getVisible() {    
    var $el = $('#foo'),
        scrollTop = $(this).scrollTop(),
        scrollBot = scrollTop + $(this).height(),
        elTop = $el.offset().top,
        elBottom = elTop + $el.outerHeight(),
        visibleTop = elTop < scrollTop ? scrollTop : elTop,
        visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
    $('#notification').text(visibleBottom - visibleTop);
}

$(window).on('scroll resize', getVisible);

Przykładowe skrzypce

edycja - mała aktualizacja, aby wykonać również logikę podczas zmiany rozmiaru okna.

Rory McCrossan
źródło
Mam problem z ustawieniem tego na wiele divsekund bez powtarzania kodu. Czy jest na to sposób?
JacobTheDev,
2
@Rev here you go: jsfiddle.net/b5DGj/1 . Oddzieliłem każdy element na jego własne wywołanie funkcji. Możesz nawet pójść dalej i zdefiniować wtyczkę, która zrobi to za Ciebie.
Rory McCrossan,
Lekko przepisałem podejście @ RoryMcCrossan do działania jako wtyczka jQuery i zamieściłem je poniżej jako odpowiedź - może mieć bardziej ogólne zastosowanie w tym formacie.
Michael.Lumley
Czasami element może być częściowo ukryty z powodu przepełnienia lub elementów nadrzędnych, niezależnie od „okna”
Nadav B
55

Oblicz ilość pikseli na jaką element (wysokość) jest w rzutni

Fiddle demo

Ta niewielka funkcja zwróci ilość pxelementu widoczną w (pionowym) oknie :

function inViewport($el) {
    var elH = $el.outerHeight(),
        H   = $(window).height(),
        r   = $el[0].getBoundingClientRect(), t=r.top, b=r.bottom;
    return Math.max(0, t>0? Math.min(elH, H-t) : Math.min(b, H));
}

Użyj jak:

$(window).on("scroll resize", function(){
  console.log( inViewport($('#elementID')) ); // n px in viewport
});

Otóż ​​to.


.inViewport()Wtyczka jQuery

jsFiddle demo

z powyższego możesz wyodrębnić logikę i stworzyć wtyczkę taką jak ta:

/**
 * inViewport jQuery plugin by Roko C.B.
 * http://stackoverflow.com/a/26831113/383904
 * Returns a callback function with an argument holding
 * the current amount of px an element is visible in viewport
 * (The min returned value is 0 (element outside of viewport)
 */
;(function($, win) {
  $.fn.inViewport = function(cb) {
     return this.each(function(i,el) {
       function visPx(){
         var elH = $(el).outerHeight(),
             H = $(win).height(),
             r = el.getBoundingClientRect(), t=r.top, b=r.bottom;
         return cb.call(el, Math.max(0, t>0? Math.min(elH, H-t) : Math.min(b, H)));  
       }
       visPx();
       $(win).on("resize scroll", visPx);
     });
  };
}(jQuery, window));

Użyj jak:

$("selector").inViewport(function(px) {
  console.log( px ); // `px` represents the amount of visible height
  if(px > 0) {
    // do this if element enters the viewport // px > 0
  }else{
    // do that if element exits  the viewport // px = 0
  }
}); // Here you can chain other jQuery methods to your selector

Twoje selektorów dynamicznie słuchać okna scrolli resizeale również zwracać wartość początkową na DOM gotowy koryta pierwsza funkcja zwrotna argumentu px.

Roko C. Buljan
źródło
Aby pracować na Androidzie Chrome, może być konieczne dodanie <meta name="viewport" content="width=your_size">sekcji head html.
userlond
@userlond dziękuję za twój wkład, ale nie jestem pewien, czy pasowałbym content="width=1259"do większości. Czy próbowałeś content="width=device-width, initial-scale=1"zamiast tego?
Roko C. Buljan,
Witryna ma starszy szablon ze stałą szerokością i nie jest responsywna ... Myślę, że Twoja sugestia będzie odpowiednia dla większości. A więc wyjaśnienie: jeśli rozwiązanie Roko C. Buljana nie działa, spróbuj dodać <meta name="viewport" content="your_content">deklarację :)
userlond
To jest tak niesamowite i tak bliskie temu, czego szukałem. Jak mogę obliczyć odległość od dołu elementów do dołu okna? Mogę więc załadować więcej treści, zanim zobaczysz koniec treści. Na przykład, jeśli elBottom <200 from windowBot, uruchom AJAX.
Thom,
1
@Thom no cóż, powyższa metoda nie zwraca żadnych innych konkretnych wartości (można ją rozwinąć, aby zwrócić więcej rzeczy niż tylko visible height pxobiekt. Zobacz to skrzypce, aby znaleźć taki pomysł: jsfiddle.net/RokoCB/6xjq5gyy (otwarta konsola programisty aby zobaczyć logi)
Roko C. Buljan
11

Oto wersja powyższego podejścia Rory'ego, z wyjątkiem tego, że została napisana tak, aby działała jako wtyczka jQuery. Może mieć bardziej ogólne zastosowanie w tym formacie. Świetna odpowiedź, Rory - dzięki!

$.fn.visibleHeight = function() {
    var elBottom, elTop, scrollBot, scrollTop, visibleBottom, visibleTop;
    scrollTop = $(window).scrollTop();
    scrollBot = scrollTop + $(window).height();
    elTop = this.offset().top;
    elBottom = elTop + this.outerHeight();
    visibleTop = elTop < scrollTop ? scrollTop : elTop;
    visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
    return visibleBottom - visibleTop
}

Można zadzwonić za pomocą:

$("#myDiv").visibleHeight();

jsFiddle

Michael Lumley
źródło
2
Nie pracuj w przypadku, gdy okno stanie się większe i blok się zmieści. Dlatego musimy zresetować wysokość bloku: $ (this) .css ('height', ''); jsfiddle.net/b5DGj/144
Max Koshel