Aplikacja internetowa na iPada: Wykryj klawiaturę wirtualną za pomocą JavaScript w Safari?

147

Piszę aplikację internetową na iPada ( nie jest to zwykła aplikacja App Store - jest napisana przy użyciu HTML, CSS i JavaScript). Ponieważ klawiatura zajmuje ogromną część ekranu, sensowna byłaby zmiana układu aplikacji, aby pasowała do pozostałej przestrzeni, gdy jest wyświetlana. Jednak nie znalazłem sposobu, aby wykryć, kiedy i czy klawiatura jest wyświetlana.

Moim pierwszym pomysłem było założenie, że klawiatura jest widoczna, gdy pole tekstowe ma fokus. Jednak gdy klawiatura zewnętrzna jest podłączona do iPada, klawiatura wirtualna nie jest wyświetlana, gdy pole tekstowe jest aktywowane.

W moich eksperymentach klawiatura nie wpłynęła również na wysokość ani wysokość przewijania żadnego z elementów DOM, a nie znalazłem żadnych zastrzeżonych zdarzeń ani właściwości, które wskazują, czy klawiatura jest widoczna.

LKM
źródło
1
Hm, ciekawy problem. Spróbuj iterować po obiektach „okna” w przeglądarce Safari na iPadzie, aby sprawdzić, czy są jakieś specjalne obiekty związane z obsługą klawiatury.
David Murdoch
@David to nie zadziała, klawiatura nie jest „oknem” Javascript.
kennytm
2
@KennyTM. Duh. Ale może istnieć flaga związana z wyświetlaniem klawiatury ekranowej w dowolnym obiekcie okna. Warto spróbować.
David Murdoch
1
Próbowałem tego. Niestety nic nie znalazłem. Porównanie wszystkich właściwości okna na trzech poziomach przed wyświetleniem klawiatury i po niej. Żadna z różnic nie wydawała się istotna jako wskaźnik dla klawiatury.
LKM
3
Czy jest na to nowsza odpowiedź?
fraxture

Odpowiedzi:

54

Znalazłem rozwiązanie, które działa, choć jest trochę brzydkie. To też nie zadziała w każdej sytuacji, ale działa dla mnie. Ponieważ dostosowuję rozmiar interfejsu użytkownika do rozmiaru okna iPada, użytkownik zwykle nie może przewijać. Innymi słowy, jeśli ustawię scrollTop okna, pozostanie on na 0.

Jeśli z drugiej strony zostanie wyświetlona klawiatura, przewijanie nagle zadziała. Mogę więc ustawić scrollTop, natychmiast przetestować jego wartość, a następnie zresetować. Oto jak to może wyglądać w kodzie, używając jQuery:

$(document).ready(function(){
    $('input').bind('focus',function() {
        $(window).scrollTop(10);
        var keyboard_shown = $(window).scrollTop() > 0;
        $(window).scrollTop(0);

        $('#test').append(keyboard_shown?'keyboard ':'nokeyboard ');
    });
});

Zwykle można się spodziewać, że nie będzie to widoczne dla użytkownika. Niestety, przynajmniej podczas pracy w symulatorze, iPad wyraźnie (choć szybko) przewija się ponownie w górę iw dół. Mimo to działa, przynajmniej w niektórych sytuacjach.

Przetestowałem to na iPadzie i wydaje się, że działa dobrze.

LKM
źródło
Mam problem z moją aplikacją internetową, gdzie gdy koncentruje się na danych wejściowych, ekran przewija się nieco w górę. Poza tym wyłączyłem przewijanie, ale nadal się to przewija. Jakieś pomysły? Dzięki [ stackoverflow.com/questions/6740253/…
Andrew Samuelsen
Jeszcze tego nie próbowałem, ale wygląda obiecująco. Czy nie .scrollTop(1)działałby równie dobrze i byłby mniej oczywisty?
ThinkingStiff
1
To zły pomysł ... Klawiatura może obsługiwać technologię Bluetooth, a wirtualna może nie być wyświetlana.
theSociableme
3
@theSociableme: Celem tego rozwiązania jest prawidłowa obsługa klawiatury bluetooth. Jeśli zignorujesz klawiatury bluetooth, ustalenie, czy wirtualna klawiatura została wyświetlona, ​​będzie łatwe, ponieważ możesz po prostu sprawdzić, czy pole było fokusem.
LKM
5
@fraxture: Nie znam wyczerpującego wyjaśnienia (jeśli zbadasz i napiszesz, z przyjemnością je przeczytam). Te dwie platformy bardzo różnie obsługują klawiatury ekranowe w swoich głównych przeglądarkach. Android Chrome zmniejsza wysokość widocznego obszaru, aby zrobić miejsce na klawiaturę, więc rozmiar strony zmienia się po wyświetleniu klawiatury. iOS Safari nakłada stronę na klawiaturę (rozmiar strony pozostaje taki sam) i zmienia sposób działania przewijania. Safari zarówno przewija stronę wewnątrz widoku, jak i jednocześnie przesuwa rzutnię, zapewniając, że dół strony znajduje się nad klawiaturą po przewinięciu do końca w dół.
LKM
32

Możesz użyć zdarzenia focusout , aby wykryć odrzucenie klawiatury. To jak rozmycie, ale bąbelki. Zapali się po zamknięciu klawiatury (ale oczywiście także w innych przypadkach). W Safari i Chrome zdarzenie można zarejestrować tylko za pomocą addEventListener, a nie za pomocą starszych metod. Oto przykład, którego użyłem do przywrócenia aplikacji Phonegap po zwolnieniu klawiatury.

 document.addEventListener('focusout', function(e) {window.scrollTo(0, 0)});

Bez tego fragmentu kontener aplikacji pozostawał w pozycji przewiniętej w górę do momentu odświeżenia strony.

Per Quested Aronsson
źródło
1
najlepsze rozwiązanie, które znalazłem dla mojego problemu
Sutulustus
1
Możesz także użyć wersji „focusin” do wykrywania otwartej klawiatury.
A.Çetin
15

może nieco lepszym rozwiązaniem jest powiązanie (z jQuery w moim przypadku) zdarzenia „blur” w różnych polach wejściowych.

Dzieje się tak, ponieważ po zniknięciu klawiatury wszystkie pola formularza są zamazane. Więc w mojej sytuacji to wycięcie rozwiązało problem.

$('input, textarea').bind('blur', function(e) {

       // Keyboard disappeared
       window.scrollTo(0, 1);

});

mam nadzieję, że to pomoże. Michele

Michele
źródło
Dzięki za tę odpowiedź. Uważam, że przydatne jest rozwiązanie problemu polegającego na tym, że klawiatura iPad Safari powodowała przesunięcie kursora obszaru tekstu (przesunięcie) poza obszar tekstu.
kennbrodhagen
14

Jeśli jest klawiatura ekranowa, skupienie uwagi na polu tekstowym znajdującym się w dolnej części okna roboczego spowoduje, że Safari przewinie pole tekstowe do widoku. Może istnieć sposób na wykorzystanie tego zjawiska do wykrycia obecności klawiatury (posiadanie małego pola tekstowego u dołu strony, które chwilowo staje się aktywne, lub coś w tym rodzaju).

ianh
źródło
1
To genialny pomysł. Znalazłem podobne rozwiązanie, które również wykorzystuje bieżącą pozycję przewijania do wykrywania wirtualnej klawiatury.
LKM
wspaniale! uratowałeś mi dzień!
aztack
11

Podczas zdarzenia fokus można przewijać poza wysokość dokumentu i magicznie wartość window.innerHeight jest zmniejszana o wysokość wirtualnej klawiatury. Zwróć uwagę, że rozmiar wirtualnej klawiatury jest inny dla orientacji poziomej i pionowej, więc musisz ją ponownie wykryć, gdy się zmieni. Odradzałbym zapamiętywanie tych wartości, ponieważ użytkownik mógł w dowolnym momencie podłączyć / odłączyć klawiaturę bluetooth.

var element = document.getElementById("element"); // the input field
var focused = false;

var virtualKeyboardHeight = function () {
    var sx = document.body.scrollLeft, sy = document.body.scrollTop;
    var naturalHeight = window.innerHeight;
    window.scrollTo(sx, document.body.scrollHeight);
    var keyboardHeight = naturalHeight - window.innerHeight;
    window.scrollTo(sx, sy);
    return keyboardHeight;
};

element.onfocus = function () {
    focused = true;
    setTimeout(function() { 
        element.value = "keyboardHeight = " + virtualKeyboardHeight() 
    }, 1); // to allow for orientation scrolling
};

window.onresize = function () {
    if (focused) {
        element.value = "keyboardHeight = " + virtualKeyboardHeight();
    }
};

element.onblur = function () {
    focused = false;
};

Zwróć uwagę, że gdy użytkownik używa klawiatury bluetooth, keyboardHeight wynosi 44, co jest wysokością [poprzedniego] [następnego] paska narzędzi.

Podczas wykrywania pojawia się niewielkie migotanie, ale nie można go uniknąć.

Hafthor
źródło
5
Właśnie wypróbowałem to w iOS 8.2 i nie działa ... czy przestało działać na jakimś etapie dla nowego iOS?
Ming
1
Dla mnie też nie zadziałało - zmiana rozmiaru nie jest uruchamiana w iOS9.3
llamerr
8

Edycja: udokumentowane przez Apple, chociaż nie mogłem go uruchomić: Zachowanie WKWebView z wyświetlaczami klawiatury : „W systemie iOS 10 obiekty WKWebView pasują do natywnego zachowania Safari, aktualizując ich właściwość window.innerHeight, gdy klawiatura jest wyświetlana, i nie wywołują zdarzenia zmiany rozmiaru ”(może użyć fokusu lub fokusu z opóźnieniem do wykrywania klawiatury zamiast zmiany rozmiaru).

Edycja: kod zakłada klawiaturę ekranową, a nie zewnętrzną. Zostawiam to, ponieważ informacje mogą być przydatne dla innych, którzy interesują się tylko klawiaturami ekranowymi. Użyj http://jsbin.com/AbimiQup/4, aby wyświetlić parametry strony.

Testujemy, czy document.activeElementjest to element, który pokazuje klawiaturę (typ wejścia = tekst, obszar tekstowy itp.).

Poniższy kod fałszuje rzeczy dla naszych celów (chociaż ogólnie nie jest to poprawne).

function getViewport() {
    if (window.visualViewport && /Android/.test(navigator.userAgent)) {
        // https://developers.google.com/web/updates/2017/09/visual-viewport-api    Note on desktop Chrome the viewport subtracts scrollbar widths so is not same as window.innerWidth/innerHeight
        return {
            left: visualViewport.pageLeft,
            top: visualViewport.pageTop,
            width: visualViewport.width,
            height: visualViewport.height
        };
    }
    var viewport = {
            left: window.pageXOffset,   // http://www.quirksmode.org/mobile/tableViewport.html
            top: window.pageYOffset,
            width: window.innerWidth || documentElement.clientWidth,
            height: window.innerHeight || documentElement.clientHeight
    };
    if (/iPod|iPhone|iPad/.test(navigator.platform) && isInput(document.activeElement)) {       // iOS *lies* about viewport size when keyboard is visible. See http://stackoverflow.com/questions/2593139/ipad-web-app-detect-virtual-keyboard-using-javascript-in-safari Input focus/blur can indicate, also scrollTop: 
        return {
            left: viewport.left,
            top: viewport.top,
            width: viewport.width,
            height: viewport.height * (viewport.height > viewport.width ? 0.66 : 0.45)  // Fudge factor to allow for keyboard on iPad
        };
    }
    return viewport;
}


function isInput(el) {
    var tagName = el && el.tagName && el.tagName.toLowerCase();
    return (tagName == 'input' && el.type != 'button' && el.type != 'radio' && el.type != 'checkbox') || (tagName == 'textarea');
};

Powyższy kod jest tylko przybliżony: jest nieprawidłowy dla podzielonej klawiatury, niezadokowanej klawiatury, klawiatury fizycznej. Zgodnie z komentarzem u góry, możesz być w stanie wykonać lepszą pracę niż podany kod w Safari (od iOS8?) Lub WKWebView (od iOS10) przy użyciu window.innerHeightwłaściwości.

Znalazłem awarie w innych okolicznościach: np. Skup się na wprowadzaniu, a następnie przejdź do ekranu głównego, a następnie wróć do strony; iPad nie powinien zmniejszać widocznego obszaru; stare przeglądarki IE nie będą działać, Opera nie działała, ponieważ Opera skupiała się na elemencie po zamknięciu klawiatury.

Jednak oznaczona odpowiedź (zmiana przewijania w celu pomiaru wysokości) ma nieprzyjemne efekty uboczne interfejsu użytkownika, jeśli można powiększać widok (lub powiększanie wymuszone w preferencjach). Nie używam innego sugerowanego rozwiązania (zmiana przewijania), ponieważ na iOS, gdy widok jest powiększalny i przewijany do skoncentrowanego wejścia, występują błędne interakcje między przewijaniem, powiększaniem i ostrością (które mogą pozostawić skoncentrowane wejście poza rzutnią - nie widoczny).

robocat
źródło
W zależności od przeglądarek innerHeight, aby wykryć przerwy na pełnym ekranie, gdy niektóre elementy są pozycjonowane absolutnie. W ogóle nie jest wiarygodny.
Udo
5

Testowane tylko na Androidzie 4.1.1:

zdarzenie rozmycia nie jest niezawodnym zdarzeniem do testowania klawiatury w górę iw dół, ponieważ użytkownik ma możliwość jawnego ukrycia klawiatury, która nie wyzwala zdarzenia rozmycia w polu, które spowodowało wyświetlenie klawiatury.

Jednak zdarzenie resize działa jak urok, jeśli klawiatura podnosi się lub opuszcza z jakiegokolwiek powodu.

Kawa:

$(window).bind "resize", (event) ->  alert "resize"

uruchamia się za każdym razem, gdy klawiatura jest pokazywana lub ukrywana z dowolnego powodu.

Należy jednak pamiętać, że w przypadku przeglądarki na Androida (zamiast aplikacji) istnieje wysuwany pasek adresu URL, który nie uruchamia się przy zmianie rozmiaru po wycofaniu, ale zmienia dostępny rozmiar okna.

user1650613
źródło
+1 za rozmycie, które nie uruchamia się po ręcznym zwolnieniu klawiatury. Zmiana rozmiaru to dobry pomysł i działałaby dobrze na urządzeniach z Androidem.
Ankit Garg
Potwierdzam, że działa to zarówno na iPhonie 5 (iOS 6.0.2), jak i iPadzie 3 (iOS 6.0).
Diego Agulló
1
Właśnie przetestowano w Chrome 41 na iOS6 w CrossBrowserTesting - zmiana rozmiaru nie jest wywoływana przez pojawianie się lub znikanie klawiatury wirtualnej.
Dan Dascalescu
4

Zamiast wykrywać klawiaturę, spróbuj wykryć rozmiar okna

Jeśli wysokość okna została zmniejszona, a szerokość nadal jest taka sama, oznacza to, że klawiatura jest włączona. W przeciwnym razie klawiatura jest wyłączona, możesz również dodać do tego, sprawdzić, czy jakieś pole wejściowe jest aktywne, czy nie.

Wypróbuj na przykład ten kod.

var last_h = $(window).height(); //  store the intial height.
var last_w = $(window).width(); //  store the intial width.
var keyboard_is_on = false;
$(window).resize(function () {
    if ($("input").is(":focus")) {
        keyboard_is_on =
               ((last_w == $(window).width()) && (last_h > $(window).height()));
    }   
});     
KA
źródło
2
Wydaje się, że to już nie działa w iOS 8. Klawiatura nakłada zawartość, aw wielu przypadkach jest ona przewijana w dół, zasłaniając początkowo zaznaczone pola wprowadzania.
Rick Strahl,
3
wysokość okna zwraca wysokość wraz z klawiaturą od iOS 7, w oknie IOS6. wysokość zmienia się po otwarciu klawiatury.
Michiel
1
Zwróć uwagę, że wysokość zmienia się również, gdy górny pasek adresu wsuwa się i wysuwa z ekranu podczas przewijania. Powinieneś dodać minimalną zmianę wysokości, powiedziałbym, 200px (nie testowane).
oriadam
1

To rozwiązanie zapamiętuje pozycję przewijania

    var currentscroll = 0;

    $('input').bind('focus',function() {
        currentscroll = $(window).scrollTop();
    });

    $('input').bind('blur',function() {
        if(currentscroll != $(window).scrollTop()){

        $(window).scrollTop(currentscroll);

        }
    });
WebsterDevelopine
źródło
1

Spróbuj tego:

var lastfoucsin;

$('.txtclassname').click(function(e)
{
  lastfoucsin=$(this);

//the virtual keyboard appears automatically

//Do your stuff;

});


//to check ipad virtual keyboard appearance. 
//First check last focus class and close the virtual keyboard.In second click it closes the wrapper & lable

$(".wrapperclass").click(function(e)
{

if(lastfoucsin.hasClass('txtclassname'))
{

lastfoucsin=$(this);//to avoid error

return;

}

//Do your stuff 
$(this).css('display','none');
});`enter code here`
Nalini Amir
źródło
1

Jak zauważyłem w poprzednich odpowiedziach gdzieś zmienna window.innerHeight jest teraz poprawnie aktualizowana na iOS10, gdy pojawia się klawiatura, a ponieważ nie potrzebuję wsparcia dla wcześniejszych wersji, wymyśliłem następujący hack, który może być nieco łatwiejszy niż omawiany "rozwiązania".

//keep track of the "expected" height
var windowExpectedSize = window.innerHeight;

//update expected height on orientation change
window.addEventListener('orientationchange', function(){
    //in case the virtual keyboard is open we close it first by removing focus from the input elements to get the proper "expected" size
    if (window.innerHeight != windowExpectedSize){
        $("input").blur();
        $("div[contentEditable]").blur();     //you might need to add more editables here or you can focus something else and blur it to be sure
        setTimeout(function(){
            windowExpectedSize = window.innerHeight;
        },100);
    }else{
        windowExpectedSize = window.innerHeight;
    }
});

//and update the "expected" height on screen resize - funny thing is that this is still not triggered on iOS when the keyboard appears
window.addEventListener('resize', function(){
    $("input").blur();  //as before you can add more blurs here or focus-blur something
    windowExpectedSize = window.innerHeight;
});

wtedy możesz użyć:

if (window.innerHeight != windowExpectedSize){ ... }

aby sprawdzić, czy klawiatura jest widoczna. Używam go już od jakiegoś czasu w mojej aplikacji internetowej i działa dobrze, ale (jak wszystkie inne rozwiązania) może się zdarzyć, że się nie powiedzie, ponieważ „oczekiwany” rozmiar nie jest poprawnie aktualizowany lub coś takiego.

Pływ
źródło
Miałem nadzieję, że tak jest, ale nie, nie jest aktualizowany.
Sam Saffron,
0

Wyszukałem trochę i nie mogłem znaleźć nic konkretnego dla słów „na klawiaturze pokazanej” lub „na klawiaturze zamkniętej”. Zobacz oficjalną listę obsługiwanych wydarzeń . Zobacz także uwagę techniczną TN2262 dotyczącą iPada. Jak zapewne już wiesz, istnieje zdarzenie związane z ciałem, onorientationchangektóre możesz połączyć, aby wykryć krajobraz / portret.

Podobnie, ale dziwne przypuszczenie ... czy próbowałeś wykryć zmianę rozmiaru? Zmiany w widocznym obszarze mogą wywołać to zdarzenie pośrednio z powodu pokazywania / ukrywania klawiatury.

window.addEventListener('resize', function() { alert(window.innerHeight); });

Co po prostu ostrzegałoby o nowej wysokości przy każdym zdarzeniu zmiany rozmiaru ...

slf
źródło
10
Niestety w moich testach klawiatura nie wyzwoliła zdarzenia zmiany rozmiaru.
LKM
0

Nie próbowałem tego osobiście, więc to tylko pomysł ... ale czy próbowałeś użyć zapytań o media w CSS, aby zobaczyć, kiedy zmienia się wysokość okna, a następnie zmienić projekt? Wyobrażam sobie, że Safari na telefon komórkowy nie rozpoznaje klawiatury jako części okna, więc mam nadzieję, że zadziała.

Przykład:

@media all and (height: 200px){
    #content {height: 100px; overflow: hidden;}
}
Janae
źródło
2
Bardzo sprytny pomysł. Niestety w moich testach pokazanie klawiatury nie miało wpływu na wartości wysokości używane do oceny zapytań o media.
LKM
Mogę potwierdzić: wysokość: 250px działała dla mnie (przynajmniej na Androidzie).
WoodrowShigeru
0

Problem polega na tym, że nawet w 2014 roku urządzenia obsługują zdarzenia zmiany rozmiaru ekranu, a także zdarzenia przewijania, niespójnie, gdy klawiatura programowa jest otwarta.

Odkryłem, że nawet jeśli używasz klawiatury bluetooth, w szczególności iOS powoduje dziwne błędy w układzie; więc zamiast wykrywać miękką klawiaturę, po prostu musiałem wybrać urządzenia, które są bardzo wąskie i mają ekrany dotykowe.

Używam zapytań o media (lub window.matchMedia ) do wykrywania szerokości i Modernizr do wykrywania zdarzeń dotykowych.

pixelbandito
źródło
0

Być może łatwiej jest mieć pole wyboru w ustawieniach aplikacji, w którym użytkownik może przełączać „podłączona klawiatura zewnętrzna?”.

Drobnym drukiem wyjaśnij użytkownikowi, że klawiatury zewnętrzne nie są obecnie wykrywalne w dzisiejszych przeglądarkach.

Ian White
źródło
1
Dodanie takiego przełącznika jest ostatecznością, która nie powinna być w ogóle uznawana za akceptowalną, chyba że nie ma innego rozwiązania, które nie uszkodzi aplikacji. To nie jest coś, co powinno blokować tworzenie działającej aplikacji.
Adam Leggett
-2

Cóż, możesz wykryć, kiedy pola wprowadzania są fokusem i znasz wysokość klawiatury. Dostępny jest również CSS, aby uzyskać orientację ekranu, więc myślę, że możesz go zhakować.

Chciałbyś jednak jakoś poradzić sobie z fizyczną klawiaturą.

mysz kaskaderska
źródło