iPhone Safari Web App otwiera łącza w nowym oknie

155

Mam problem z siecią po dodaniu ikony do ekranu głównego. Jeśli Internet jest uruchamiany z ekranu głównego, wszystkie łącza otworzą się w nowym oknie w Safari (i utracą funkcjonalność pełnego ekranu). Jak mogę temu zapobiec? Nie mogłem znaleźć żadnej pomocy, tylko to samo pytanie bez odpowiedzi.

Pavel Linkesch
źródło
3
Możesz teraz użyć scopeparametru w manifest.json. Zobacz moją odpowiedź, aby uzyskać więcej informacji. Przetestowałem to w iOS 11.3 i działa.
Amir Raminfar
3
Aby powtórzyć, dla każdego, kto zmaga się z otwieraniem Safari w systemie iOS 11.3, zapoznaj się z odpowiedzią @ AmirRaminfar tutaj: stackoverflow.com/a/49604315/32055
Chris Haines

Odpowiedzi:

110

Znalazłem rozwiązanie JavaScript w ramach iWebKit :

var a=document.getElementsByTagName("a");
for(var i=0;i<a.length;i++)
{
    a[i].onclick=function()
    {
        window.location=this.getAttribute("href");
        return false
    }
}
Pavel Linkesch
źródło
25
Mówiąc wprost i wyraźnie: iOS traktuje linki w aplikacjach internetowych jako coś, co powinno być otwierane w przeglądarce Safari, a zmiana lokalizacji javascript jako czynność w aplikacji, która może działać w aplikacji internetowej. Powyższy kod działa, ponieważ zapobiega domyślnemu zachowaniu linków, zastępując je wywołaniem js nav.
Oskar Austegard
6
Czy jest przykład odwrotny? Zmuszanie aplikacji internetowej na iPhone'a do otwierania strony w Safari, mimo że jest to zmiana lokalizacji javascript?
tkahn
1
@Pavel dziękuję za wspomnienie o iwebkit :). Pomaga uzyskać trochę ruchu: D
cmplieger
3
[].forEach.call(document.links, function(link) { link.addEventListener("click", function(event) { event.preventDefault(); window.location = this.href; }) });
alex
1
Czy ma to jakieś skutki uboczne?
pingu
94

Inne rozwiązania tutaj albo nie uwzględniają linków zewnętrznych (które prawdopodobnie chcesz otworzyć zewnętrznie w Safari) lub nie uwzględniają linków względnych (bez domeny w nich).

Projekt html5 mobile-boilerplate zawiera linki do tego streszczenia, które ma dobrą dyskusję na ten temat: https://gist.github.com/1042026

Oto ostateczny kod, który wymyślili:

<script>(function(a,b,c){if(c in b&&b[c]){var d,e=a.location,f=/^(a|html)$/i;a.addEventListener("click",function(a){d=a.target;while(!f.test(d.nodeName))d=d.parentNode;"href"in d&&(d.href.indexOf("http")||~d.href.indexOf(e.host))&&(a.preventDefault(),e.href=d.href)},!1)}})(document,window.navigator,"standalone")</script>
rmarscher
źródło
To działa świetnie, z wyjątkiem jednej strony, strony „Kontakt” dla naszej firmy. Zamiast wyświetlać stronę, otwiera aplikację „Mapy” i wskazuje nasze biuro. Co może być tego przyczyną i jak możemy to naprawić?
Jonathan,
@Jonathan Nie jestem pewien. Czy to się nie dzieje, jeśli usuniesz ten skrypt? Może zamieść link do swojej strony? Albo otwórz nowe pytanie, może być lepiej.
rmarscher
@rmarscher Dzieje się tak tylko wtedy, gdy uruchamiasz kod, który podałeś, a nie bez niego. Sam jestem programistą stron internetowych i nie rozumiem, dlaczego miałby obsługiwać łącze w ten sposób. Nie mam adresu URL strony, ponieważ obecnie nie uruchamia ona kodu, więc go nie zauważysz. Dotyczy to również zwykłego Safari, a nie tylko samodzielnego. Dziękuję za odpowiedź!
Jonathan,
Powinna to być akceptowana odpowiedź i zadziałała urokiem na moim pełnoekranowym kliencie iPad1 wykonanym za pomocą PHPRunner, umieszczając kod w nagłówku. Nie jestem pewien, dlaczego jest tak zaciemniony, ponieważ wydaje się dość zwięzły fragment kodu, który można napisać czytelnie bez dużego narzutu BW ... to tylko wybredne i ogólnie naprawdę chcę podziękować.
sradforth,
4
To psuje rzeczy Bootstrappy, takie jak linki href = "#" używane przez funkcje js
Sean,
47

Jeśli używasz jQuery, możesz:

$("a").click(function (event) {
    event.preventDefault();
    window.location = $(this).attr("href");
});
David H.
źródło
1
Proszę wyjaśnić, dlaczego .live () może być lepsze?
ajcw
8
live spowoduje powiązanie wydarzenia ze wszystkimi linkami, w tym z tymi, które jeszcze nie istnieją, kliknięcie spowoduje powiązanie tylko z tymi, które obecnie istnieją
msaspence
dzięki! ratownik. Właśnie spędziłem godziny, próbując dowiedzieć się, dlaczego safari cały czas się ładuje.
Steve,
1
+1 ode mnie - używane this.hrefzamiast rzutowania do obiektu jQuery, ale dzięki za tę odpowiedź. Działa na iOS6.
Fenton,
17
.live () jest przestarzała od jQuery 1.7 i usunięta od 1.9 . Zamiast tego użyj $ (document) .on ('click', 'a', function () {...}).
Tom Davies
21

To działa dla mnie na iOS 6.1 i z linkami Bootstrap JS (tj. Menu rozwijane itp.)

$(document).ready(function(){
    if (("standalone" in window.navigator) && window.navigator.standalone) {
      // For iOS Apps
      $('a').on('click', function(e){
        e.preventDefault();
        var new_location = $(this).attr('href');
        if (new_location != undefined && new_location.substr(0, 1) != '#' && $(this).attr('data-method') == undefined){
          window.location = new_location;
        }
      });
    }
  });
Sean
źródło
1
+1. To faktycznie sprawdza, czy używasz aplikacji internetowej przed naprawieniem linków.
Trolley
1
Działa w iOS 8.0.2! Dzięki
Joel Murphy
1
@sean Mam inną aplikację internetową uruchomioną na iPadzie, która używa mapy obrazu jako href i ten kod nie działa. Działa dobrze dla wszystkich innych linków. Jakieś pomysły, jak sprawić, by ten kod działał z mapami obrazu? Próbowałem skopiować cały fragment i zamienić $('a').on('click'funkcję (e) {`na $('area').on('click'funkcję (e) {`, ale to też nie działa. Jakieś pomysły?
nicienie
Jeśli masz już zdefiniowane funkcje klikania za apomocą href="#", możesz być bardziej szczegółowy w selektorze jquery, np.$('a[href!="#"]')
cjk
13

To jest stare pytanie i wiele rozwiązań tutaj wykorzystuje JavaScript. Od tego czasu wydano iOS 11.3 i możesz teraz używać elementu członkowskiego zakresu . Element członkowski zakresu to adres URL podobny do tego, w "/"którym wszystkie ścieżki w tym zakresie nie otwierają nowej strony.

Element członkowski zakresu to ciąg, który reprezentuje zakres nawigacji w kontekście aplikacji tej aplikacji sieci Web.

Oto mój przykład:

{
  "name": "Test",
  "short_name": "Test",
  "lang": "en-US",
  "start_url": "/",
  "scope": "/",
  ...
}

Możesz również przeczytać więcej na ten temat tutaj . Polecam również użycie generatora, który zapewni taką funkcjonalność.

Jeśli określisz zakres, wszystko będzie działać zgodnie z oczekiwaniami, podobnie jak w Androidzie, miejsca docelowe poza zakresem zostaną otwarte w Safari - z przyciskiem Wstecz (małym na pasku stanu) do twojego PWA.

Amir Raminfar
źródło
7
Niestety nie sądzę, aby można było objąć zakresem inne witryny (np. Loginy OAuth w innej domenie).
bhollis
Hej @Amir, utworzyłem pwa za pomocą Angulara, a na Androidzie pojawia się komunikat „Dodaj do ekranu głównego”, ponieważ wygląda na to, że poprawnie odczytuje mój plik manfest.json. Jednak na IOs Chrome / Safari nie ma zachęty ... jakieś pomysły?
ustad
5

Na podstawie odpowiedzi Davidsa i komentarza Richardsa należy przeprowadzić kontrolę domeny. W przeciwnym razie w Twojej aplikacji internetowej zostaną otwarte również linki do innych witryn internetowych.

$('a').live('click', function (event)
{      
    var href = $(this).attr("href");

    if (href.indexOf(location.hostname) > -1)
    {
        event.preventDefault();
        window.location = href;
    }
});
Thomas Kekeisen
źródło
Dobry dodatek do powyższych rozwiązań. Potrzebne było sprawdzenie domeny, aby uniemożliwić ludziom otwieranie zewnętrznych witryn w aplikacji. Działa również na iOS 5.
Ian
dla mnie działa również na iOS 5. Czasami problem może dotyczyć pamięci podręcznej. Podczas testowania różnych podejść nie mogłem zmusić iOS do unieważnienia pamięci podręcznej i pobrania nowej wersji plików JS (Safari odebrało zmiany, ale nie więcej po dodaniu aplikacji do ekranu głównego). Pomogła zmiana portu mojego serwera deweloperskiego. Jeśli masz ustawioną wartość max-age = 0 (lub równoważną), prawdopodobnie nie będzie to miało na Ciebie wpływu.
Łukasz Korzybski
5

Jeśli używasz jQuery Mobile, zobaczysz nowe okno, używając atrybutu data-ajax = 'false'. W rzeczywistości stanie się to za każdym razem, gdy ajaxEnabled zostanie wyłączony, będąc przez i zewnętrznym łączem, przez ustawienie $ .mobile.ajaxEnabled lub przez posiadanie atrybutu target = ''.

Możesz to naprawić, używając tego:

$("a[data-ajax='false']").live("click", function(event){
  if (this.href) {
    event.preventDefault();
    location.href=this.href;
    return false;
  }
});

(Podziękowania dla Richarda Poole'a za metodę live () - nie działało z bind ())

Jeśli globalnie wyłączyłeś ajaxEnabled, musisz porzucić [data-ajax = 'false'].

Zajęło mi to dość dużo czasu, aby się zorientować, ponieważ spodziewałem się, że będzie to problem specyficzny dla jQuery Mobile, podczas gdy w rzeczywistości to linkowanie Ajax faktycznie blokowało nowe okno.

Jason Prawn
źródło
Doskonale, uratowałeś mnie :)
saulob
3

Ten kod działa na iOS 5 (działał dla mnie):

W tagu head:

<script type="text/javascript">
    function OpenLink(theLink){
        window.location.href = theLink.href;
    }
</script>

W linku, który chcesz otworzyć w tym samym oknie:

<a href="(your website here)" onclick="OpenLink(this); return false"> Link </a>

Mam ten kod z tego komentarza: metatagi aplikacji internetowej na iPhone'a

SerinEleven
źródło
Z jakiegoś powodu myślę, że jest to najłatwiejsze do zrozumienia.
Jerrybibo
3

Może powinieneś zezwolić na otwieranie linków w nowym oknie, gdy cel jest jawnie ustawiony na „_blank”:

$('a').live('click', function (event)
{      
    var href = $(this).attr("href");

    // prevent internal links (href.indexOf...) to open in safari if target
    // is not explicitly set_blank, doesn't break href="#" links
    if (href.indexOf(location.hostname) > -1 && href != "#" && $(this).attr("target") != "_blank")
    {
        event.preventDefault();
        window.location = href;
    }

});
daformat
źródło
Wielkie dzięki! To jedyny kod, który działał dla iOS5 z Twitter Bootstrap. To jednak nie działa na produkcji.
Chim Kan
Mmm nie jestem taki pewien, dlaczego nie miałby działać w produkcji, ale myślę, że to coś innego. Daj mi znać!
daformat
2

Możesz też prawie normalnie tworzyć linki:

<a href="#" onclick="window.location='URL_TO_GO';">TEXT OF THE LINK</a>

Możesz też usunąć hash tag i href, wszystko, co robi, wpływa na wygląd.

JuR
źródło
2

Oto, co zadziałało na mnie na iOS 6 (bardzo nieznaczna adaptacja odpowiedzi rmarschera):

<script>                                                                
    (function(document,navigator,standalone) {                          
        if (standalone in navigator && navigator[standalone]) {         
            var curnode,location=document.location,stop=/^(a|html)$/i;  
            document.addEventListener("click", function(e) {            
                curnode=e.target;                                       
                while (!stop.test(curnode.nodeName)) {                  
                    curnode=curnode.parentNode;                         
                }                                                       
                if ("href" in curnode && (curnode.href.indexOf("http") || ~curnode.href.indexOf(location.host)) && curnode.target == false) {
                    e.preventDefault();                                 
                    location.href=curnode.href                          
                }                                                       
            },false);                                                   
        }                                                               
    })(document,window.navigator,"standalone")                          
</script>
bjh
źródło
2

Jest to nieco zaadaptowana wersja Seana, która blokowała przycisk Wstecz

// this function makes anchor tags work properly on an iphone

$(document).ready(function(){
if (("standalone" in window.navigator) && window.navigator.standalone) {
  // For iOS Apps
  $("a").on("click", function(e){

    var new_location = $(this).attr("href");
    if (new_location != undefined && new_location.substr(0, 1) != "#" && new_location!='' && $(this).attr("data-method") == undefined){
      e.preventDefault();
      window.location = new_location;
    }
  });
}

});

Richard Turner
źródło
1

Dla tych z Twitter Bootstrap i Rails 3

$('a').live('click', function (event) {
  if(!($(this).attr('data-method')=='delete')){
    var href = $(this).attr("href");
    event.preventDefault();
    window.location = href; 
  }   
});

Usuwanie linków nadal działa w ten sposób.

wouf
źródło
1

Wolę otwierać wszystkie linki w trybie samodzielnej aplikacji internetowej z wyjątkiem tych, które mają target = "_ blank". Oczywiście przy użyciu jQuery.

$(document).on('click', 'a', function(e) {
    if ($(this).attr('target') !== '_blank') {
        e.preventDefault();
        window.location = $(this).attr('href');
    }
});
Alex Haas
źródło
1

Jednym z obejść, które zastosowałem w aplikacji internetowej na iOS, było to, że wszystkie linki (które były przyciskami w CSS) tworzyłem przyciski przesyłania. Więc otworzyłem formularz, który wysłał do linku docelowego, a następnie input type = "submit" Nie jest to najlepszy sposób, ale to jest to, co odkryłem, zanim znalazłem tę stronę.

dster77
źródło
1

W przypadku użytkowników JQuery Mobilepowyższe rozwiązania powodują przerwanie wyskakującego okna dialogowego. Zachowa to linki w aplikacji internetowej i pozwoli na wyświetlanie wyskakujących okienek.

$(document).on('click','a', function (event) {
    if($(this).attr('href').indexOf('#') == 0) {
        return true;
    }
    event.preventDefault();
    window.location = $(this).attr('href');     
});

Można to również zrobić poprzez:

$(document).on('click','a', function (event){
    if($(this).attr('data-rel') == 'popup'){
        return true;
    }
    event.preventDefault();
    window.location = $(this).attr('href');     
});
Hexchaimen
źródło
0

Oto, czego użyłbym dla wszystkich linków na stronie ...

document.body.addEventListener(function(event) {
    if (event.target.href && event.target.target != "_blank") {
        event.preventDefault();
        window.location = this.href;                
    }
});

Jeśli używasz jQuery lub Zepto ...

$("body").on("click", "a", function(event) {
   event.target.target != "_blank" && (window.location = event.target.href);
});
Alex
źródło
-3

Możesz po prostu usunąć ten metatag.

<meta name="apple-mobile-web-app-capable" content="yes">
Soohwan Park
źródło