Link directly to a tab in Bootstrap - conflict of scripts

Hi all,

I use the below code to prevent links from jumping:

$('a[href*="#"]')
  .not('[href="#"]')
  .not('[href="#0"]')
  .not('[href="#intro"]')
  .not('[href="#ingredients"]')
  .not('[href="#sweets"]')
  .not('[href="#drinks"]')
  .not('[href="#traditional"]')
  
  .click(function(event) {
    if (
      location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') 
      && 
      location.hostname == this.hostname
    ) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
      if (target.length) {
        event.preventDefault();
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 1000, function() {
          var $target = $(target);
          $target.focus();
          if ($target.is(":focus")) {
            return false;
          } else {
            $target.attr('tabindex','-1');
            $target.focus();
          };
        });
      }
    }
  });

For some reason it is in conflict with any script I have found to be able to link to a tab directly,
These are the scripts I tried to use combined with the first script.

Also tried this:
Code:
$(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'));
    });
});

and this:

Code:
$('a[data-toggle="tab"]').click(function (e) {
    e.preventDefault();
    $(this).tab('show');
});

$('a[data-toggle="tab"]').on("shown.bs.tab", function (e) {
    var id = $(e.target).attr("href");
    localStorage.setItem('selectedTab', id)
});

var selectedTab = localStorage.getItem('selectedTab');
if (selectedTab != null) {
    $('a[data-toggle="tab"][href="' + selectedTab + '"]').tab('show');
}

and this:

Code:
var hash = location.hash.replace(/^#/, '');
if (hash) {
    $('.nav-tabs a[href="#' + hash + '"]').tab('show');
} 
$('.nav-tabs a').on('shown.bs.tab', function (e) {
    window.location.hash = e.target.hash;
})

and this:

Code:
// Javascript to enable link to tab
var hash = document.location.hash;
var prefix = "tab_";
if (hash) {
    $('.nav-tabs a[href="'+hash.replace(prefix,"")+'"]').tab('show');
} 

// Change hash for page-reload
$('.nav-tabs a').on('shown', function (e) {
    window.location.hash = e.target.hash.replace("#", "#" + prefix);
});

Is there a possibility to have these scripts work alongside, like best friends?
My aim is to have any navigation with target # to run smoothly (first script), alongside with the linking inside tab-content (four potential scripts provided but all conflict with the first).
Thanks.

I don’t know if this is of any use but we had a similar question in the forum which resulted in this demo.

The smooth scrolling is done with css (no need for js at all). The js opens the tab only,
It may be something you can work with.

Thanks, Paul.

I replaced this to css for smooth scrolling:

scroll-behavior: smooth;

And this to jss for the back-to-top:

        $(document).on('click', '.back-to-top', function () {
            $("html,body").animate({
                scrollTop: 0
            }, 20);
        });

        $(window).on("scroll", function() {
        var ScrollTop = $('.back-to-top');
        if ($(window).scrollTop() > 500) {
            ScrollTop.fadeIn(1000);
        } else {
            ScrollTop.fadeOut(1000);
        }

On this page, I would like to be able to link from another page to

  • spices, herbs, fruits, etc
  • saffron, raisins, pistachios, etc

How do I do that, please?

The css should have been able to handle the smooth scroll back to the top. You just need a target id on the body or one of the first elements on the page so that you can link to it.

You mean that from another page you would like to link directly to one of the tabs and have it displayed automatically?

If so then you’d need to add the #linktotab to the url in your other page and then in the tab page itself you’d need to use some js to detect the hash and then open the tab.

JS isn’t really my thing so hopefully a js guru will drop by and correct me if I’m wrong but I believe something like this is needed.

  var getTab = $(location.hash).filter("a[data-toggle='tab']");
  if (getTab.length) {
    $("a[href='" + location.hash + "']").click();
  }

I added it to my codepen and it seems to work.

1 Like

Thanks Paul.
Indeed the scrolling-to-top can be achieved by just adding a target id, but that shows the back-to-top-arrow immediately, as I just want the arrow to be seen after scrolling down the page a bit. How to achieve that with css, please?

I added #spices to a link on another page:
And added this to the main js:

var getTab = $(location.hash).filter("a[data-toggle='tab']");
  if (getTab.length) {
    $("a[href='" + location.hash + "']").click();
  }

But it does not seem to work.

I don’t see the code anywhere here in main.js?

I assume you added the js to the destination page? (I’m sure you did but just checking ;))

You would need js where checking on scrolling is concerned. First add this CSS after your original code:

.back-to-top {
  opacity: 0;
  transition: opacity 0.5s ease;
}
.back-to-top.show {
  opacity: 1;
}

Then do something like this in the JS.

//Get the button
let mybutton = document.querySelector(".back-to-top");

// When the user scrolls down 50px from the top of the document, show the button
window.onscroll = function () {
  scrollFunction();
};

function scrollFunction() {
  if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
    mybutton.classList.add("show");
  } else {
    mybutton.classList.remove("show");
  }
}

Rough demo:

Thanks Paul.

The back-to-top works like a charm, great!
Regarding the tabs: I had added your code in main.js, but as it was not working, I had removed it.

But so the be clear: all I have to do for it to work is:
1/ add this to main.js:

-> under $(document).ready(function () {

var getTab = $(location.hash).filter("a[data-toggle='tab']");
  if (getTab.length) {
    $("a[href='" + location.hash + "']").click();
  }

2/ change the link on anther page

I have done the above, but it does not work.
Please click here on “spices and flavourings” to see (should jump to second tab “spices”).

Ok I can see you have added it now but I’m not sure why its not working as it works exactly the same in my demo above. It seems to be returning undefined so there must be a conflict somewhere.

I’m just on my way out now so maybe a JS Guru could step in :slight_smile: (@rpg_digital are you around?)

Just having a brief look, if you open your console in chrome(f12), you’ll see an error being thrown

Uncaught TypeError: $(…).niceSelect is not a function

The specific code is on line 113 (or thereabouts)

  /*--------------------------------------------------
      select onput
  ---------------------------------------------------*/
  $(document).ready(function() {
      $('select').niceSelect(); ←
  });

Might be an idea to tackle that or comment it out whilst you work on implementing Paul’s script.

There is also another error in about.php

Uncaught TypeError: Cannot read properties of undefined (reading ‘firstlevel’)

<body onload="initListGroup('chainedmenu', document.listmenu0.firstlevel, document.listmenu0.secondlevel, 'saveme')">

document.listmenu0 is undefined

I can’t be sure, but maybe these bugs are hindering things.

1 Like

Thanks RPG.

Just deleted this from main.js

  $(document).ready(function() {
      $('select').niceSelect(); ←
  });

But still no luck.
I need the “body onload” for the two-level search in the sidebar on certain pages (f.ex. recipes - but not all pages), so I removed it to see if the direct linking would work. But it still does not work, so the issue must be somewhere else.

I think this may solve the problem but will need some html adjustments.

Add an ID to the anchor tab links like so.

 <ul class="nav nav-tabs">
              <li><a id="intro-tab" class="active" data-toggle="tab" href="#intro">intro</a></li>
              <li><a id="spices-tab" data-toggle="tab" href="#spices">spices</a></li>
              <li><a id="herbs-tab" data-toggle="tab" href="#herbs">herbs</a></li>
              <li><a id="fruits-tab" data-toggle="tab" href="#fruits">fruits</a></li>
              <li><a id="nuts-tab" data-toggle="tab" href="#nuts">nuts</a></li>
              <li><a id="map-tab" data-toggle="tab" href="#map">origin map</a></li>
            </ul>

Then use this js in your document ready.

 var getTab = $(location.hash).filter("a[data-toggle='tab']");
      if (getTab.length === 1) {
        $(getTab).click();
      }

On the other page you now link to the id referenced in those anchors.
e.g. #spices-tab

Not #spices

That seems to work as far as I can tell from here :slight_smile:

Yes yes yes, works perfectly! Thanks!
Now next steps:

  • how do I link from another page to for example sumac, that is part of spices
  • how do I link from one tab to another tab

Sorry just on my way to bed:)

I’ll be back tomorrow afternoon but hopefully someone else will have jumped in by then :slight_smile:

Glad we got the first part working anyway.

Not sure if I am helping or hindering here.

I have just setup a simple test with an index.php page and a persian.php page

index.php just has some simple links

<ul>
    <li><a href='./spices.php#sideglass-tab'>Side glass</a></li></li>
    <li><a href='./spices.php#glassreardoors-tab'>Glass rear doors</a></li></li>
    <li><a href='./spices.php#doorhandleslocks-tab'>Door Handles and Locks</a></li>
</ul>

To get these links to scroll and open the correct tab on the spices page I had to make a small amend to Paul’s jquery

$(document).ready(() => {
  const hash = location.hash;
  const getTab = $(location);

  // setup the 'click' listeners first
  $('a[data-toggle="tabnew"]')
    .on('click', function () {
      const hash = $(this).attr('href');
      $(hash).tab('show');
    })

  if (getTab.length) {
    $(`a[href='${hash}']`).click(); // ← then this will work
  }
})

A simple fix of setting up the click listeners first before firing click.

One other thing that did stand out to me was the ‘scroll to top’ button. I thought it was broken until I reaslised that I had to click on the small arrow inside the circle. I think an improvement would be to make the anchor the circle and not it’s container.

Thanks, RPG, but Paul’s script works perfectly. I implemented yours as a test and it doesn’t seem to do the trick.
Regarding the “scroll to top”, indeed it would be better to have the whole circle clickable.

This is the current code:

    <div class="back-to-top">
        <a href="#topscroll"><i class="fa fa-angle-up"></i></a>
    </div>

and

        let mybutton = document.querySelector(".back-to-top");
        window.onscroll = function () {
          scrollFunction();
        };
        function scrollFunction() {
          if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 300) {
            mybutton.classList.add("show");
          } else {
            mybutton.classList.remove("show");
          }
        }

What needs to be adjusted then, please?

As I say I wasn’t sure if I was hindering. I am confused though.

You say when you click a link from another page it opens the correct tab in the spices page? I don’t see how.

$(document).ready(() => {
  var getTab = $(location.hash).filter("a[data-toggle='tab']");
  console.log("Tab length - " + getTab.length);

  if (getTab.length) {
    $("a[href='" + location.hash + "']").click();
  }

  $('a[data-toggle="tabnew"]').on("click", function () {
    const hash = $(this).attr("href");
    $(hash).tab("show");
  });
});

I guess we have to take asynchronisity into account, but if getTab has length e.g. a usable hash then a click event is fired before this bit of code has been assigned to click

const hash = $(this).attr("href");
$(hash).tab("show");

Certainly in my test going from say index.php → spices.php#sideglass-tab, it didn’t work. The correct tab was not shown.

Anyway maybe best I leave this to Paul, as he seems to be having a bit more success :slight_smile:

Edit:

If I go to

https://www.parisaspersiankitchen.com/persian-cooking-essentials.php#spices

or

https://www.parisaspersiankitchen.com/persian-cooking-essentials.php#spices-tab

It doesn’t open the spices tab. So it appears if this is the updated site that it isn’t currently working.

Thanks for your explanation, RPG.
If I go to this page and click on “spices and flavourings” with link to

https://www.parisaspersiankitchen.com/persian-cooking-essentials.php#spices-tab

Then the spices tab opens.
Strange that you see it differently. Cache maybe? Or there is another script on the page that initiates what you mention in your code (it is a template, so I have not put the code together but added stuff like the menu)?

Maybe you can help out with the following:

  • how do I link from another page to for example sumac, which is part of spices
  • how do I link from one tab (sumac) to another tab (walnuts)
  • how to make the whole “scroll-to-top” circle clickable

If you add this extra code it will make the anchor fill the circle.

.back-to-top a{
display:block;
width:100%;
height:100%;
background-color: var(--main-color);
border-radius:50%;
}

.back-to-top:hover a{
  box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.4);
}

That link goes straight to here for me.

The revised code was in post #10.

 var getTab = $(location.hash).filter("a[data-toggle='tab']");
      if (getTab.length === 1) {
        $(getTab).click();
      }

I made the mistake in the original of linking to the hidden tab rather than the anchor id.

Maybe @rpg_digital could offer some help as it seems to me that you would need to set up some sort of logic as you will first need to automatically click spices which allows the bootstrap to open and set up the tab and then you would need to automatically click the sumac link to travel to the destination.

Maybe you could add a data-parent="#spices-tab" attribute to the sublinks which supplies the id of the parent tabs anchor and then click that followed by clicking the sublink id. So for example if the has was #sumac you’d check whether it had a data-parent attribute and if so automatically click the value of the data attribute followed by clicking #sumac.

I think the above scenario may also work for that.

One of those days @bruno19, just tried again and it’s working :+1:

1 Like

Of to work now I’m afraid. I’ll maybe have a look later tonight.

1 Like