La aplicación web iPhone Safari abre enlaces en una nueva ventana

155

Tengo un problema con la web después de agregar el icono a la pantalla de inicio. Si la web se inicia desde la pantalla de inicio, todos los enlaces se abrirán en una nueva ventana en Safari (y perderán la funcionalidad de pantalla completa). ¿Cómo puedo prevenirlo? No pude encontrar ayuda, solo la misma pregunta sin respuesta.

Pavel Linkesch
fuente
3
Ahora puede usar el scopeparámetro en manifest.json. Vea mi respuesta para más detalles. Lo he probado en iOS 11.3 y funciona.
Amir Raminfar
3
Para reiterar, para cualquiera que tenga problemas con iOS 11.3 abriendo Safari, consulte la respuesta de @ AmirRaminfar aquí: stackoverflow.com/a/49604315/32055
Chris Haines

Respuestas:

110

Encontré la solución de JavaScript en el marco de 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
fuente
25
Para decir lo obvio y hacer esto explícito: iOS trata los enlaces en las aplicaciones web como algo que debería abrirse en Safari, y la ubicación de javascript cambia como una acción en la aplicación que se puede guardar en la aplicación web. El código anterior funciona porque evita el comportamiento predeterminado del enlace, reemplazándolo con una llamada de navegación js.
Oskar Austegard
66
¿Hay un ejemplo de lo contrario? ¿Obligar a una aplicación web de iPhone a abrir una página en Safari aunque sea un cambio de ubicación de JavaScript?
tkahn
1
@Pavel gracias por mencionar iwebkit :). Ayuda a conseguir algo de tráfico: D
cmplieger
3
[].forEach.call(document.links, function(link) { link.addEventListener("click", function(event) { event.preventDefault(); window.location = this.href; }) });
alex
1
¿Tiene esto algún efecto secundario?
pingu
94

Las otras soluciones aquí no tienen en cuenta los enlaces externos (que probablemente desee abrir externamente en Safari) o no tienen en cuenta los enlaces relativos (sin el dominio en ellos).

El proyecto html5 mobile-boilerplate enlaza con esta esencia que tiene una buena discusión sobre el tema: https://gist.github.com/1042026

Aquí está el código final que se les ocurrió:

<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
fuente
Esto funciona muy bien, excepto por una página, la página "Contáctenos" de nuestra empresa. En lugar de mostrar la página, abre la aplicación "Mapas" y señala nuestra oficina. ¿Qué podría estar causando esto y cómo podemos solucionarlo?
Jonathan
@ Jonathan No estoy seguro. ¿No sucede si eliminas este script? Tal vez publicar un enlace a su sitio? O abra una nueva pregunta, que podría ser mejor.
rmarscher
@rmarscher Esto solo sucede cuando se ejecuta el código que proporcionó y no sin él. Soy desarrollador web y no entiendo por qué manejaría el enlace de esta manera. No tengo una URL de página porque actualmente no está ejecutando el código, por lo que no lo notarás. Además, afecta al Safari regular también, y no solo de forma independiente. ¡Gracias por tu respuesta!
Jonathan
Esta debería ser la respuesta aceptada y funcionó de maravilla en mi cliente de pantalla completa iPad1 hecho con PHPRunner colocando el código en el encabezado. No estoy seguro de por qué está tan ofuscado, ya que parece un código bastante conciso que podría escribirse de forma legible sin mucha sobrecarga de BW ... aunque eso es solo ser exigente y, en general, realmente quiero decir gracias.
sradforth
44
Esto rompe cosas de Bootstrappy como los enlaces href = "#" que usan las funciones js
Sean
47

Si está utilizando jQuery, puede hacer:

$("a").click(function (event) {
    event.preventDefault();
    window.location = $(this).attr("href");
});
David H.
fuente
1
Por favor, explique por qué .live () podría ser mejor?
ajcw
8
live vinculará el evento a todos los enlaces, incluidos los que aún no existen, el clic solo se vinculará a los que existen actualmente
msaspence
¡Gracias! salvador de la vida. Solo pasé horas tratando de descubrir por qué el safari se estaba cargando todo el tiempo.
Steve
1
+1 de mi parte: utilizado en this.hreflugar de enviarlo a un objeto jQuery, pero gracias por esta respuesta. Funciona en iOS6.
Fenton
17
.live () está en desuso a partir de jQuery 1.7 , y se eliminó a partir de 1.9 . Use $ (document) .on ('click', 'a', function () {...}) en su lugar.
Tom Davies
21

Esto funciona para mí en iOS 6.1 y con enlaces Bootstrap JS (es decir, menús desplegables, etc.)

$(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
fuente
1
+1. Esto realmente comprueba si está utilizando una aplicación web antes de corregir los enlaces.
Trolley
1
Funciona en iOS 8.0.2! Gracias
Joel Murphy
1
@sean Tengo otra aplicación web ejecutándose en un iPad que usa un mapa de imagen como href y este código no funciona ... Funciona bien para todos los demás enlaces. ¿Alguna idea de cómo hacer que este código funcione con mapas de imágenes? He intentado copiar todo el fragmento y reemplazar $('a').on('click', función (e) {`con $('area').on('click', función (e) {` pero eso tampoco parece funcionar. ¿Algunas ideas?
nematoth
En caso de que ya tiene la función de clic definidas en ala href="#"continuación puedes ser más específico en el selector de jQuery, por ejemplo$('a[href!="#"]')
cjk
13

Esta es una vieja pregunta y muchas de las soluciones aquí están usando JavaScript. Desde entonces, se ha lanzado iOS 11.3 y ahora puede usar el miembro de alcance . El miembro de ámbito es una URL como "/"donde todas las rutas bajo ese ámbito no abrirán una nueva página.

El miembro de ámbito es una cadena que representa el ámbito de navegación del contexto de aplicación de esta aplicación web.

Aquí está mi ejemplo:

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

También puede leer más sobre esto aquí . También recomiendo usar el generador que proporcionará esta funcionalidad.

Si especifica el alcance, todo funciona como se esperaba similar a Android, los destinos fuera del alcance se abrirán en Safari, con un botón de retroceso (el pequeño en la barra de estado) a su PWA.

Amir Raminfar
fuente
77
Desafortunadamente, no creo que pueda incluir otros sitios web (como los inicios de sesión de OAuth en otro dominio) en el alcance.
bhollis
Hola @Amir, he creado un pwa usando Angular y en Android, aparece el mensaje "Agregar a la pantalla de inicio", ya que parece que está leyendo mi manfest.json correctamente. Sin embargo, en IOs Chrome / Safari, no hay avisos ... ¿alguna idea?
Ustad
5

Según la respuesta de Davids y el comentario de Richards, debe realizar una verificación de dominio. De lo contrario, los enlaces a otros sitios web también se abrirán en su aplicación web.

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

    if (href.indexOf(location.hostname) > -1)
    {
        event.preventDefault();
        window.location = href;
    }
});
Thomas Kekeisen
fuente
Buena adición a las soluciones anteriores. Necesitaba una verificación de dominio para evitar que las personas abrieran sitios externos en la aplicación. Además, funciona en iOS 5.
Ian
Funciona en iOS 5 para mí también. El problema a veces puede ser con el caché. Mientras probaba diferentes enfoques, no pude forzar a iOS a invalidar su caché y recuperar una nueva versión de los archivos JS (Safari recogió los cambios, pero no más después de agregar la aplicación a la pantalla de inicio). Cambiar el puerto de mi servidor de desarrollo ayudó. Si tienes max-age = 0 set (o equivalente), entonces esto probablemente no te afectará.
Lukasz Korzybski
5

Si usa jQuery Mobile, experimentará la nueva ventana cuando use el atributo data-ajax = 'false'. De hecho, esto sucederá siempre que ajaxEnabled esté desactivado, por un enlace externo, por una configuración $ .mobile.ajaxEnabled o por tener un atributo target = ''.

Puedes arreglarlo usando esto:

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

(Gracias a Richard Poole por el método live (), no funcionaba con bind ())

Si ha desactivado ajaxEnabled globalmente, deberá soltar [data-ajax = 'false'].

Esto me tomó bastante tiempo para darme cuenta, ya que esperaba que fuera un problema específico de jQuery Mobile donde, de hecho, fue el enlace Ajax lo que realmente prohibió la nueva ventana.

Jason Langostino
fuente
Perfecto, me salvaste :)
saulob
3

Este código funciona para iOS 5 (funcionó para mí):

En la etiqueta de la cabeza:

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

En el enlace que desea que se abra en la misma ventana:

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

Obtuve este código de este comentario: metaetiquetas de aplicaciones web para iPhone

SerinEleven
fuente
Por alguna razón, creo que esto es lo más fácil de comprender.
Jerrybibo
3

Tal vez debería permitir abrir enlaces en una nueva ventana cuando el objetivo también se establezca explícitamente en "_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
fuente
¡Muchas gracias! Este es el único código que funcionó para iOS5 con Twitter Bootstrap. Sin embargo, no funciona en la producción.
Chim Kan
Mmm, no estoy tan seguro de por qué no funcionaría en la producción, pero creo que es otra cosa. ¡Déjame saber!
daformat
2

También puedes hacer enlaces casi normalmente:

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

Y puede eliminar la etiqueta hash y href, todo lo que hace afecta la apariencia.

JuR
fuente
2

Esto es lo que funcionó para mí en iOS 6 (muy leve adaptación de la respuesta de rmarscher):

<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
fuente
2

Esta es una versión ligeramente adaptada de Sean que impedía el botón de retroceso

// 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
fuente
1

Para aquellos con Twitter Bootstrap y Rails 3

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

Eliminar enlaces todavía funcionan de esta manera.

wouf
fuente
1

Prefiero abrir todos los enlaces dentro del modo de aplicación web independiente, excepto los que tienen target = "_ blank". Usando jQuery, por supuesto.

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

Una solución alternativa que utilicé para una aplicación web iOS fue que hice que todos los enlaces (que eran botones de CSS) formaran botones de envío. Así que abrí un formulario que se publicó en el enlace de destino, luego ingresé type = "submit" No es la mejor manera, pero es lo que descubrí antes de encontrar esta página.

dster77
fuente
1

Para aquellos que usan JQuery Mobile, las soluciones anteriores rompen el diálogo emergente. Esto mantendrá los enlaces dentro de la aplicación web y permitirá ventanas emergentes.

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

También podría hacerlo por:

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

Esto es lo que usaría para todos los enlaces en una página ...

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

Si está utilizando jQuery o Zepto ...

$("body").on("click", "a", function(event) {
   event.target.target != "_blank" && (window.location = event.target.href);
});
alex
fuente
-3

Simplemente puede eliminar esta metaetiqueta.

<meta name="apple-mobile-web-app-capable" content="yes">
Parque Soohwan
fuente