JavaScript help needed, please

Discussion in 'Programming & Software Development' started by TonyR, Jul 26, 2019.

  1. TonyR

    TonyR Member

    Joined:
    Jun 29, 2001
    Messages:
    691
    Location:
    NSW South Coast
    If you load https://www.gillandtony.com/Geocaching/stats1.html you will see my geocaching statistics. At the top is a row of tabs ("Home", "Special", etc.) which display different parts of the statistics. Half-way down the "Special" tab is a list of caches which I have given Favourite Points. I want to have a direct link to this part of the statistics. I have added <a name='favourites'></a> at the appropriate point and have tried accessing that with a link including #favourites.

    Unfortunately, this doesn't work because the favourites are not on the default tab display.

    I have been told I need to add parameters to the link and then add some javascript to the html page to recognise the parameter, open the appropriate tab and then go to the correct bit.

    Since I know almost nothing about JavaScript programming I have come here for help.

    Could someone please let me know how to get this working?

    Thanks

    Tony
     
  2. kimbecause

    kimbecause Member

    Joined:
    Oct 22, 2002
    Messages:
    16
    Location:
    West Aus
    This script will do what you need. Add it to the bottom of your page as it needs to run after the page is loaded.

    <script>
    if (window.location.hash == '#favourites') {
    clickEvent = document.createEvent ('MouseEvents');
    clickEvent.initEvent ("mousedown", true, true);
    document.getElementById('tab2').dispatchEvent (clickEvent);
    clickEvent.initEvent ("mouseup", true, true);
    document.getElementById('tab2').dispatchEvent (clickEvent);
    document.querySelectorAll('a[name=favourites]')[0].scrollIntoView();
    }
    </script>

    What it does is check if you have #favourites on the URL. If you do, it clicks on tab2 (Specials) and then scrolls to the 'favourites' anchor.

    If you used onclick instead of onmousedown for the tabs then this script would require a lot less code. But don't change it now :)
     
    Last edited: Jul 26, 2019
  3. OP
    OP
    TonyR

    TonyR Member

    Joined:
    Jun 29, 2001
    Messages:
    691
    Location:
    NSW South Coast
    Thanks very much for that. I'll add that tomorrow.

    Is there any way to generalise it? I was thinking of something like putting "?tab=tab2&#favourites" onto the end of the URL and trying to interrogate that inside the html. That way I could add other targets in future

    I don't have any control over the basic HTML. It is generated by a program written by someone else. I can add bits and pieces into the middle (or at the end) but the way tabs work is outside my control. I'll pass your idea about onclick to him, but I doubt it will change.

    Thanks again

    Tony
     
  4. kimbecause

    kimbecause Member

    Joined:
    Oct 22, 2002
    Messages:
    16
    Location:
    West Aus
    Hey Tony,

    Sure thing. Here is the code -

    <script>
    if (window.location.hash) {
    tmpHashAr = window.location.hash.substring(2).split('/');
    clickEvent = document.createEvent ('MouseEvents');
    clickEvent.initEvent ("mousedown", true, true);
    document.getElementById('tab2').dispatchEvent (clickEvent);
    clickEvent.initEvent ("mouseup", true, true);
    document.getElementById(tmpHashAr[0]).dispatchEvent (clickEvent);
    document.querySelectorAll('a[name=' + tmpHashAr[1] + ']')[0].scrollIntoView();
    }
    </script>

    This will look for a URL like https://www.gillandtony.com/Geocaching/stats1.html#/tab2/favourites

    The hash after the URL is in the format /<tab id>/<Anchor name>

    The tabs have ids set by the program that generates the HTML. The first tab Home is tab1, Special is tab2, etc. The anchor name can be whatever you set, in this case favourites. The forward slashes just split up the variables so the code can parse them.

    Don't worry too much about the mouseclick. It would just mean that 5 of the lines from this code could be condensed to 1 line. Now that we've written the code though it doesn't matter :). Changing it now would mean we would have to write more code to fix it.

    The best place for this code is at the bottom of the <body> tag, so just before the line that says </body>

    If you have any questions or problems just reply to this thread.

    Kim
     
    TonyR likes this.
  5. OP
    OP
    TonyR

    TonyR Member

    Joined:
    Jun 29, 2001
    Messages:
    691
    Location:
    NSW South Coast
    Hi, Kim

    Thanks so much for that. It works perfectly. If you are ever in the Ulladulla area, let me know and I'll buy you a beverage-of-your-choice or two.

    One small question: there is a line
    document.getElementById('tab2').dispatchEvent (clickEvent);
    Should that actually say 'tab2' or should it be something like tmpHashAr[0]?

    Would you have any objection to me sharing this script so it is available to everyone?

    Thanks again

    Tony
     
    Last edited: Jul 27, 2019
  6. General_Cartman

    General_Cartman Member

    Joined:
    Apr 28, 2005
    Messages:
    2,735
    Location:
    The Confedaracah!
    tmpHashAr = window.location.hash.substring(2).split('/');
    clickEvent = document.createEvent ('MouseEvents');

    should be

    var tmpHashAr = window.location.hash.substring(2).split('/');
    var clickEvent = document.createEvent ('MouseEvents');

    unless for some reason you want those variables to be global.
     
  7. OP
    OP
    TonyR

    TonyR Member

    Joined:
    Jun 29, 2001
    Messages:
    691
    Location:
    NSW South Coast
    I can't see why those variables should be global, so I'll make that change. Thanks for pointing it out.

    By default the file opens with tab1 displayed. If we wanted to force a different tab to display then I think that just the first part of this script would be needed.

    I'm guessing that a URL like https://www.gillandtony.com/Geocaching/stats1.html#/tab3/ would achieve that, provided the script was modified to check if the second argument was omitted. I'm no JavaScript programmer, but looking at the rest of the code, would it be as simple as replacing the line

    document.querySelectorAll('a[name=' + tmpHashAr[1] + ']')[0].scrollIntoView();

    with

    If (tmpHashAr[1] != "") {
    document.querySelectorAll('a[name=' + tmpHashAr[1] + ']')[0].scrollIntoView();
    }

    So that the line only gets executed if the second argument is not null?

    Also, would the trailing / on the URL be needed?

    Thanks again to you both for helping out.

    Tony
     
  8. OP
    OP
    TonyR

    TonyR Member

    Joined:
    Jun 29, 2001
    Messages:
    691
    Location:
    NSW South Coast
    I've just modified the script as above and it now works perfectly. Exactly what I was looking for

    Thanks again

    Tony
     
  9. kimbecause

    kimbecause Member

    Joined:
    Oct 22, 2002
    Messages:
    16
    Location:
    West Aus
    Sorry for the late reply. I've been meening to post this for a week now.

    The var won't matter because the code is in not contained in a local scope. So with or without the var it will be attached to the global scope. The correct way would be to wrap this code in a function, and make it a local variable. In this case it shouldn't matter, but I have fixed it in the attached code.

    The trailing slash on the url should be optional. The code coverts the # portion to an array of paths. The code only uses the first two path sections, so slashes after that are ignored.

    The first slash is not actually used, but is required by this code. I put it in so it would look like a path, but the first slash gets removed in this line

    window.location.hash.substring(2).split('/')

    If you change the 2 to a 1, you won't need the first / on the path. The reason it is there is because most JavaScript libraries that use this path format ('/path1/path2') so I just made it how I was used to.

    The line that you suggested

    If (tmpHashAr[1] != "") {

    The code will still work the same without this check - it will just throw an error to the console because it can't find the anchor name you are looking for. It will throw a similar error if you have an anchor name in the path that doesn't exist on the page. After a bit of testing, I found it is better to wrap this in a try / catch, because it will still throw an error if the anchor can't be found.

    I also missed an error that that the mousedown event is still sent to tab2, which could cause some strangeness. That was on this line -

    document.getElementById('tab2').dispatchEvent (clickEvent);

    The following script localises the variables in a self invoking function, puts in the try/catch, and fixes that bug.

    <script>
    (function() {
    if (window.location.hash) {
    var tmpHashAr = window.location.hash.substring(2).split('/');
    var clickEvent = document.createEvent ('MouseEvents');
    clickEvent.initEvent ("mousedown", true, true);
    document.getElementById(tmpHashAr[0]).dispatchEvent (clickEvent);
    clickEvent.initEvent ("mouseup", true, true);
    document.getElementById(tmpHashAr[0]).dispatchEvent (clickEvent);
    try {
    document.querySelectorAll('a[name=' + tmpHashAr[1] + ']')[0].scrollIntoView();
    }
    catch (e) {}
    }
    })();
    </script>

    Also feel free to modify / use / pass on the code however you like.
     
    Last edited: Aug 3, 2019
  10. OP
    OP
    TonyR

    TonyR Member

    Joined:
    Jun 29, 2001
    Messages:
    691
    Location:
    NSW South Coast
    Thanks for getting back to me.

    What does the try/catch do? I'm guessing that it attempts to scroll to the second argument but when it isn't there it traps the error silently? Why is that a better approach than trsting the second argument and skipping the line?

    Interesting language JavaScript!

    Cheers

    Tony
     
  11. kimbecause

    kimbecause Member

    Joined:
    Oct 22, 2002
    Messages:
    16
    Location:
    West Aus
    Yes the try catch will catch the exception and in this case do nothing. In JavaScript if an error occurs it will print the error to the console and stop running the script. You can view the console with ctrl-shift-j or cmd-shift-j on Mac. Most sites will throw at least a few errors. Some generate hundreds and still work :)

    With this script the potential error is on the last line of the script, so there is no functional difference between dealing with it or not. We should deal with it though in case more code is added after.

    If we check for the second path section being undefined or empty, it could still be defined but the anchor name does not exist on the page. In that case it would throw a different error. So instead we use a try catch which will catch either exception and ignore it. In this case there is no issue with ignoring the error.

    Most sites (not all) will use a library like jQuery that covers over these niggly errors from JavaScript and makes a lot of things easier. I didn't use jQuery here because it wasn't previously used and I didn't want to add a whole extra library just for this.
     
  12. General_Cartman

    General_Cartman Member

    Joined:
    Apr 28, 2005
    Messages:
    2,735
    Location:
    The Confedaracah!
    Doh, of course. I'm so used to using IIFEs I completely missed there wasn't one in your script.
     
  13. kimbecause

    kimbecause Member

    Joined:
    Oct 22, 2002
    Messages:
    16
    Location:
    West Aus
    And that was the fix :)
     
    General_Cartman likes this.

Share This Page

Advertisement: