Twitter Bootstrap - Pestañas - La URL no cambia

109

Estoy usando Twitter Bootstrap y sus "pestañas".

Tengo el siguiente código:

<ul class="nav nav-tabs">
    <li class="active"><a data-toggle="tab" href="#add">add</a></li>
    <li><a data-toggle="tab" href="#edit" >edit</a></li>
    <li><a data-toggle="tab" href="#delete" >delete</a></li>
</ul>

Las pestañas funcionan correctamente, pero en la URL no se agrega #add, #edit, #delete.

Cuando elimino data-togglela URL cambia, pero las pestañas no funcionan.

¿Alguna solución para esto?

Ivanka Todorova
fuente
1
Complemento que corrige esto: github.com/timabell/jquery.stickytabs
Tim Abell
Esta es una pregunta similar a stackoverflow.com/q/18999501/873282
koppor

Respuestas:

269

Prueba este código. Agrega la pestaña href a la url + abre la pestaña según el hash en la carga de la página:

$(function(){
  var hash = window.location.hash;
  hash && $('ul.nav a[href="' + hash + '"]').tab('show');

  $('.nav-tabs a').click(function (e) {
    $(this).tab('show');
    var scrollmem = $('body').scrollTop() || $('html').scrollTop();
    window.location.hash = this.hash;
    $('html,body').scrollTop(scrollmem);
  });
});
tomaszbak
fuente
1
Funciona muy bien. +1. Pero cuál es la razón de "hash && $ .." del selector es mostrar pestaña, pero qué significa "hash &&". Gracias
richardhell
4
&&significa ANDque si la izquierda de eso equivale a verdadero ( hashno es 0, nulo o vacío) y la derecha equivale a verdadero, entonces haz algo, sin embargo, la línea termina con un, ;por lo que no hay nada que hacer antes ;y no importa si un La pestaña se muestra o no y no importa lo que .tab('show')regrese, igual lo intentará. Al evaluar la lógica de esta manera, si la primera parte de la ecuación (antes &&) es falsa, no hay necesidad de continuar evaluando, por lo que no se mostrará la pestaña. Podría lograr el mismo efecto así:if(hash){ $('ul.nav a[href="' + hash + '"]').tab('show'); }
vahanpwns
1
TL; DR: a && b;significa probar ay luego, solo si es cierto, inténtalo b. Las cadenas son verdaderas si no están vacías.
vahanpwns
8
¿Cómo puedo lograr que vuelva a cargar la página en la parte superior cuando uso la URL "#"? En este momento, se desplaza hacia abajo hasta donde comienzan las pestañas. Me gustaría desplazarme hacia la parte superior de la página real
kibaekr
1
funciona bien pero tuvo que reemplazar "$ ('. nav-tabs a'). click (" por "$ ('# navtabs a'). on ('shown.bs.tabs," en Bootstrap 3
Zzirconium
49

Hay tres componentes necesarios para una solución completa:

  1. Muestra la pestaña correcta cuando se carga la página si hay un hash en la URL.
  2. Cambiar el hash en la URL cuando se cambia la pestaña.
  3. Cambie la pestaña cuando el hash cambie en la URL (botones de retroceso / avance) y asegúrese de que la primera pestaña se cicla correctamente.

No creo que ninguno de los comentarios aquí, ni la respuesta aceptada, manejen todos estos escenarios.

Como hay bastante involucrado, creo que es mejor como un pequeño complemento de jQuery: https://github.com/aidanlister/jquery-stickytabs

Puedes llamar al complemento así:

$('.nav-tabs').stickyTabs();

Hice una publicación de blog para esto, http://aidanlister.com/2014/03/persisting-the-tab-state-in-bootstrap/

Aidan
fuente
Le di su propio repositorio de git: github.com/timabell/jquery.stickytabs - muchas gracias por el complemento, funciona de maravilla .
Tim Abell
+1 para esta solución, ya que también funciona en la navegación anterior / siguiente, lo que la solución @tomaszback 'no funciona. ¡Gracias!
bitfidget
Esto es genial y funciona bien, pero no cuando cargo la página con un hash en la URL. Seguiré investigando para asegurarme de que sea algo que hice mal (es muy probable). Sin embargo, voy a darle un +1
MattBoothDev
27

Usando data-toggle="tab" le pide al complemento que maneje las pestañas, que mientras lo hace llama preventDefaultal evento ( código en github ).

Puede usar la activación de su propia pestaña y dejar que el evento pase:

$('.nav-tabs a').click(function (e) {
    // No e.preventDefault() here
    $(this).tab('show');
});

Y debes eliminar el atributo data-toggle="tab"

Consulte el documento si tiene dudas.

Sherbrow
fuente
11

Como sus elementos de anclaje ya cambian el hash de la URL, escuchar el evento onhashchange es otra buena opción. Como @flori ya mencionó, este método funciona bien con el botón de retroceso del navegador.

var tabs$ = $(".nav-tabs a");

$( window ).on("hashchange", function() {
    var hash = window.location.hash, // get current hash
        menu_item$ = tabs$.filter('[href="' + hash + '"]'); // get the menu element

    menu_item$.tab("show"); // call bootstrap to show the tab
}).trigger("hashchange");

Finalmente, activar el evento justo después de definir el oyente también ayudará a mostrar la pestaña derecha al cargar la página.

halilb
fuente
3

Mi solución a tu problema:

Primero agregue un nuevo data-valueatributo a cada aenlace, así:

<ul class="nav nav-tabs">
    <li class="active">
        <a data-toggle="tab" href="#tab-add" data-value="#add">add</a>
    </li>
    <li>
        <a data-toggle="tab" href="#tab-edit" data-value="#edit">edit</a>
    </li>
    <li>
        <a data-toggle="tab" href="#tab-delete" data-value="#delete">delete</a>
    </li>
</ul>

En segundo lugar, cambie los identificadores de las pestañas, por ejemplo, de adda tab-add, también actualice hrefs para incluir el tab-prefijo:

<div class="tab-content">
    <div class="tab-pane" id="tab-add">Add panel</div>
    <div class="tab-pane" id="tab-edit">Edit panel</div>
    <div class="tab-pane" id="tab-delete">Panel panel</div>
</div>

Y finalmente el código js:

<script type="text/javascript">
    $(function () {
        var navTabs = $('.nav-tabs a');
        var hash = window.location.hash;
        hash && navTabs.filter('[data-value="' + hash + '"]').tab('show');

        navTabs.on('shown', function (e) {
            var newhash = $(e.target).attr('data-value');
            window.location.hash = newhash;
        });
    })
</script>

Aquí está jsFiddle , pero no funciona debido al iframe utilizado por jsFiddle, pero puede leer la fuente de mi solución.

psulek
fuente
3

El mismo problema con CakePHP con Bootstrap 3 Tabs, además, cuando envío con una URL externa y un ancla, salta a la sección perdiendo mi menú, después de revisar muchas soluciones hicieron esto ...

gracias a: http://ck.kennt-wayne.de/2012/dec/twitter-bootstrap-how-to-fix-tabs

$(document).ready(function()
 {  
//Redirecciona al tab, usando un prefijo al cargar por primera vez, al usar redirect en cake #_Pagos    
//Redirecciona al tab, usando #Pagos
/* Automagically jump on good tab based on anchor; for page reloads or links */
 if(location.hash) 
  {
     $('a[href=' + location.hash + ']').tab('show');         
  }


//Para evitar el bajar al nivel del tab, (al mostrarse el tab subimos)
$('a[data-toggle="tab"]').on('shown.bs.tab', function(e) 
{
    location.hash = $(e.target).attr('href').substr(1);
    scrollTo(0,0);      
});



//Actualiza el URL con el anchor o ancla del tab al darle clic
 /* Update hash based on tab, basically restores browser default behavior to
 fix bootstrap tabs */
$(document.body).on("click", "a[data-toggle]", function(event) 
  {
    location.hash = this.getAttribute("href");
  });


//Redirecciona al tab, al usar los botones de regresar y avanzar del navegador.
/* on history back activate the tab of the location hash if exists or the default tab if no hash exists */   
$(window).on('popstate', function() 
{
  //Si se accesa al menu, se regresa al tab del perfil (activo default), fixed conflict with menu
  //var anchor = location.hash || $("a[data-toggle=tab]").first().attr("href");
  var anchor = location.hash;
  $('a[href=' + anchor + ']').tab('show');

});   

});//Fin OnReady
Henry Gabriel González Montejo
fuente
2

También hay una forma sencilla de reaccionar ante los cambios de hash (por ejemplo, botón de retroceso). Simplemente agregue un detector de eventos hashchange a la solución tomaszbak:

$(function(){
  // Change tab on load
  var hash = window.location.hash;
  hash && $('ul.nav a[href="' + hash + '"]').tab('show');

  $('.nav-tabs a').click(function (e) {
    $(this).tab('show');
    var scrollmem = $('body').scrollTop();
    window.location.hash = this.hash;
    $('html,body').scrollTop(scrollmem);
  });

  // Change tab on hashchange
  window.addEventListener('hashchange', function() {
    var changedHash = window.location.hash;
    changedHash && $('ul.nav a[href="' + changedHash + '"]').tab('show');
  }, false);
});
Flori
fuente
1

Estoy usando Bootstrap 3.3. * Y ninguna de las soluciones anteriores me ayudó, incluso marcada como la respuesta uno. Acabo de agregar el siguiente script y trabajé.

$(function(){
    var hash = document.location.hash;
    if (hash) {
       $('.navbar-nav a[href="' + hash + '"]').tab('show');
    }
    $('a[data-toggle="tab"]').on('click', function (e) {
       history.pushState(null, null, $(this).attr('href'));
    });
});

Espero que esto te ayude.

Abhimanyu
fuente
0

Más simple, asumiendo que está utilizando la data-toggle="tab"sintaxis documentada que se describe aquí :

$(document).on('shown.bs.tab', function(event) {
  window.location.hash = $(event.target).attr('href');
});
Fred
fuente
Esto cambia la URL, pero no puede ir allí cuando se hace clic en un enlace para abrir una pestaña específica
Khrys
0

Para aquellos que usan Ruby on Rails o cualquier otro script del lado del servidor, querrán usar la anchoropción en rutas. Esto se debe a que una vez que se carga la página, no tiene el hash de URL disponible. Deberá proporcionar la pestaña correcta a través de su enlace o envío de formulario.

<%= form_for @foo, url: foo_path(@foo, anchor: dom_id(foo)) do |f| %>
# Or
<%= link_to 'Foo', foo_path(@foo, anchor: dom_id(foo)) %>

Si está utilizando un prefijo para evitar que la ventana salte a la identificación:

<%= form_for @foo, url: foo_path(@foo, anchor: "bar_#{dom_id(foo)}") do |f| %>

Entonces CoffeeScript:

  hash = document.location.hash
  prefix = 'bar_'
  $('.nav-tabs a[href=' + hash.replace(prefix, '') + ']').tab 'show' if hash
  $('.nav-tabs a').on 'shown.bs.tab', (e) ->
    window.location.hash = e.target.hash.replace '#', '#' + prefix

O JavaScript:

var hash, prefix;

hash = document.location.hash;
prefix = 'bar_';

if (hash) {
  $('.nav-tabs a[href=' + hash.replace(prefix, '') + ']').tab('show');
}

$('.nav-tabs a').on('shown.bs.tab', function(e) {
  window.location.hash = e.target.hash.replace('#', '#' + prefix);
});

Esto debería funcionar en Bootstrap 3.

Mohamad
fuente
0

Gracias a @tomaszbak por la solución original, sin embargo, dado que mis ediciones fueron rechazadas y aún no puedo comentar en su publicación, dejaré mi versión ligeramente mejorada y más legible aquí.

Básicamente hace lo mismo, pero si soluciona el problema de compatibilidad entre navegadores que tuve con el suyo.

$(document).ready(function(){
   // go to hash on window load
   var hash = window.location.hash;
   if (hash) {
       $('ul.nav a[href="' + hash + '"]').tab('show');
   }
   // change hash on click
   $('.nav-tabs a').click(function (e) {
       $(this).tab('show');
       if($('html').scrollTop()){
           var scrollmem = $('html').scrollTop(); // get scrollbar position (firefox)
       }else{
           var scrollmem = $('body').scrollTop(); // get scrollbar position (chrome)
       }
       window.location.hash = this.hash;
       $('html,body').scrollTop(scrollmem); // set scrollbar position
   });
});
Fuxy
fuente
Rejected no funcionaba para mí (tal vez solo era la versión de Firefox). De todos modos, hice un cambio a var scrollmem = $ ('body'). ScrollTop () || $ ('html'). scrollTop (); ¡Gracias!
tomaszbak
1
Si. Cuéntame sobre eso en el momento en que dijiste que me di cuenta de que era una cosa del navegador y rápidamente entré en Chrome para asegurarme. Seguro que desearía que hubiera una forma de comentar sobre las revisiones de edición.
Fuxy
0

Aquí hay una opción que se usa history.pushStatepara que pueda usar el historial de su navegador para volver a una pestaña anterior

  $(document).ready(function() {
  // add a hash to the URL when the user clicks on a tab
  $('a[data-toggle="tab"]').on('click', function(e) {
    history.pushState(null, null, $(this).attr('href'));
  });
  // navigate to a tab when the history changes
  window.addEventListener("popstate", function(e) {
    var activeTab = $('[href=' + location.hash + ']');
    if (activeTab.length) {
      activeTab.tab('show');
    } else {
      $('.nav-tabs a:first').tab('show');
    }
  });
});
Adam Hey
fuente
0

Nada de lo anterior funcionó para mí, así que así es como hice que funcionara el mío:

  1. Agregar class="tab-link"a todos los enlaces que necesitan esta funcionalidad
  2. Agregue el siguiente código a su javascript:
    window.addEventListener
    (
        'popstate',
        función (evento)
        {
            tab_change ();
        }
    );

    función tab_change ()
    {
        var hash = window.location.hash;
        hash && $ ('ul.nav a [href = "' + hash + '"]') .tab ('mostrar');

        $ ('.tab-link') .click
        (
            función (e)
            {
                $ (esto) .tab ('mostrar');

                var scrollmem = $ ('cuerpo') .scrollTop () || $ ('html') .scrollTop ();
                window.location.hash = this.hash;
                $ ('html, cuerpo') .scrollTop (scrollmem);

                $ ('ul.nav-tabs a [href = "' + window.location.hash + '"]') .tab ('show');
            }
        );
    }

Simonl
fuente
0

Entendí que OP dijo usar JQuery, pero simplemente puede usar Vanilla JS para hacer eso:

document.querySelectorAll('ul.nav a.nav-link').forEach(link => {
    link.onclick = () => window.history.pushState(null, null, link.attributes.href.value);
});
RousseauAlexandre
fuente
-2

Esto también corrige una pestaña extremadamente oscura a href que no se activa cuando se usa jquery y bootstrap

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> 

<script type="text/javascript">
$('.nav-tabs a').click(function (e) {
    // No e.preventDefault() here.
    $(this).tab('show');
});
</script>

<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/js/bootstrap.min.js"></script>
Rayo
fuente
5
-1 No veo cómo esta respuesta agrega ningún valor a las respuestas anteriores dadas hace 3 meses con exactamente el mismo código en la respuesta. Por lo tanto, no es una respuesta útil en mi humilde opinión.
No