Vivaldi

  • Browser
  • Mail
  • News
  • Community
  • About

Navigation

    • Browser
    • Mail
    • News
    • Community
    • About
    • Register
    • Login
    • Search
    HomeBlogsForumHelpThemes
    • Home
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    1. Home
    2. Desktop
    3. Customizations & Extensions
    4. Modifications
    5. Tab Scroll

    Tab Scroll

    Modifications
    javascript
    15
    50
    4664
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • luetage
      luetage last edited by pafflick

      I've been trying to emulate a new Opera feature as described here: https://forum.vivaldi.net/topic/26784/return-to-previous-position-after-scroll-to-top. The mod scrolls to top of the current page when clicking a tab and scrolls back to initial position when clicking the tab again. You will need a custom.js file to implement this modification. Instructions: https://forum.vivaldi.net/topic/10549/modding-vivaldi/. Please disable the "minimise active tab" feature in vivaldi://settings/tabs/, or the mod won't work as intended. You can only use one or the other.


      custom.js

      // Tab Scroll
      // version 2022.4.0
      // https://forum.vivaldi.net/topic/27856/tab-scroll
      // Clicking on an active tab scrolls page to top, clicking it again returns to
      // previous scroll position. Credits to tam710562 from Vivaldi Forum for coming
      // up with the sessionStorage solution, which made this possible.
      
      (function tabScroll() {
        function exit(tab) {
          tab.removeEventListener("mousemove", exit);
          tab.removeEventListener("click", trigger);
        }
      
        function trigger(tab) {
          chrome.scripting.executeScript({
            target: { tabId: Number(tab.parentNode.id.replace(/\D/g, "")) },
            function: script,
          });
          exit(tab);
        }
      
        function react(e, tab) {
          if (
            tab.parentNode.classList.contains("active") &&
            e.which === 1 &&
            !e.shiftKey &&
            !e.ctrlKey &&
            !e.altKey &&
            !e.metaKey
          ) {
            tab.addEventListener("mousemove", exit(tab));
            tab.addEventListener("click", trigger(tab));
          }
        }
      
        const script = () => {
          let offset = window.pageYOffset;
          if (offset > 0) {
            window.sessionStorage.setItem("tabOffset", offset);
            window.scrollTo(0, 0);
          } else {
            window.scrollTo(0, window.sessionStorage.getItem("tabOffset") || 0);
          }
        };
      
        let appendChild = Element.prototype.appendChild;
        Element.prototype.appendChild = function () {
          if (
            arguments[0].tagName === "DIV" &&
            arguments[0].classList.contains("tab-header")
          ) {
            setTimeout(
              function () {
                const ts = (event) => react(event, arguments[0]);
                arguments[0].addEventListener("mousedown", ts);
              }.bind(this, arguments[0])
            );
          }
          return appendChild.apply(this, arguments);
        };
      })();
      

      Opera blog post with feature announcement – https://blogs.opera.com/desktop/2018/02/opera-51/

      History

      • 2019-07-02: Back to URL independent version, because it works on all sites (especially Vivaldi Forum). For the other version look deeper into this topic
      • 2021-02-18: Fixes for 3.7, works with 2 level tab stacks now
      • 2021-07-10: Use scripting.executescript
      • 2022.4.0 Update functions

      github ◊ vfm

      Hadden89 K stardepp 3 Replies Last reply Reply Quote 15
      • Hadden89
        Hadden89 @luetage last edited by Hadden89

        @luetage Nice, testing.
        Good idea. I think would better a hover button to scroll up or use the favicon to do that and prevent accidental scroll-to-top.

        But it works 😛

        Patience is the key to get the Vivaldi Spree | Unsupported Extensions | DarkChromeCSS | Github

        luetage 1 Reply Last reply Reply Quote 0
        • luetage
          luetage @Hadden89 last edited by

          @hadden89 Would be far easier to do this with a dedicated button, but the point was to emulate the request. I'm not too concerned about accidental clicking, because why should you click the title of an active tab, if you didn't want to use the functionality to go to top? And even if you click it by accident, you can just return to the previous position by clicking again 😛

          github ◊ vfm

          1 Reply Last reply Reply Quote 1
          • luetage
            luetage last edited by

            I found out that I can just put the scrollTab.js code into custom.js. It's far easier to use one file, especially if you are using a bash script to reload the files on update. OP has been updated.

            github ◊ vfm

            1 Reply Last reply Reply Quote 1
            • LonM
              LonM Moderator last edited by

              Thanks for making this. I have learned interesting things:

              • You can use chrome.* APIs in the context of a chrome.tabs.executeScript(). I guess chrome treats the executed script like a content script.
              • The callback function can take in the output of execution of the script. I guess I completely skipped over that part in the docs.

              💻 Windows 10 64-bit Sopranos Builds • en-GB • 🗳 vote for features • 🕵️‍♀️ Code of Conduct • 🐞 Report bugs

              luetage 1 Reply Last reply Reply Quote 0
              • luetage
                luetage @LonM last edited by

                @lonm Yeah, I was surprised too. Especially that it's so easy to use the local storage when needed. The callback function from the execute script part was a fluke, I found it on stackoverflow by accident. I really wanted to avoid using multiple files and passing messages.

                https://stackoverflow.com/questions/41577988/chrome-tabs-executescript-how-to-get-result-of-content-script

                github ◊ vfm

                1 Reply Last reply Reply Quote 0
                • luetage
                  luetage last edited by luetage

                  This mod stopped working on the latest snapshot (2.0), because Vivaldi registers a click on a tab with target tab-header now. Therefore the script could be simplified and it's easier triggering the function.

                  OP has been updated.

                  github ◊ vfm

                  1 Reply Last reply Reply Quote 1
                  • luetage
                    luetage last edited by

                    Mod had problems with the latest update on snapshot, click on the title is being recognized as title again, not tab-header. I updated the script in OP, hopefully this version is somewhat more change/future-proof.

                    github ◊ vfm

                    1 Reply Last reply Reply Quote 0
                    • tam710562
                      tam710562 last edited by

                      @luetage I think it's recommended to use window.sessionStorage or a variable in chrome.tabs.executeScript instead of using chrome.storage.local. It will save the scroll bar position by tab instead of only one value in multiple tabs

                      function tabScroll(event) {
                          var target = event.target;
                          if (target.parentNode.classList.contains('tab-header')) {
                              target = target.parentNode;
                          }
                          if (target.classList.contains('tab-header')) {
                              chrome.tabs.executeScript({
                                  code: 'var offset=window.pageYOffset;if(offset>0){window.sessionStorage.setItem("vivaldi-offset",offset);window.scrollTo(0,0);}else{window.scrollTo(0,window.sessionStorage.getItem("vivaldi-offset")||0);}'
                              });
                          }
                      }
                      
                      // Loop waiting for the browser to load the UI. You can call all functions from just one instance.
                      
                      setTimeout(function wait() {
                          const browser = document.getElementById('browser');
                          if (browser) {
                              document.body.addEventListener('click', tabScroll);
                          } else {
                              setTimeout(wait, 300);
                          }
                      }, 300);
                      
                      luetage 2 Replies Last reply Reply Quote 1
                      • luetage
                        luetage @tam710562 last edited by

                        @tam710562 I had no idea about session storage. This is pretty cool – of course it's better when everything is set for each tab individually – this brings it almost to feature parity with the native Opera version.

                        github ◊ vfm

                        1 Reply Last reply Reply Quote 0
                        • luetage
                          luetage @tam710562 last edited by luetage

                          @tam710562 The current problem is that the tabScroll triggers on selecting a tab, when it really should only trigger on an active tab. The click event listener can't catch this, but gladly mousedown can.

                          function tabScroll(event) {
                              var target = event.target;
                              if (target.parentNode.classList.contains('tab-header')) {
                                  target = target.parentNode;
                              }
                              if (target.classList.contains('tab-header') && target.parentNode.classList.contains('active')) {
                                  chrome.tabs.executeScript({
                                      code: 'var offset=window.pageYOffset;if(offset>0){window.sessionStorage.setItem("vivaldi-offset",offset);window.scrollTo(0,0);}else{window.scrollTo(0,window.sessionStorage.getItem("vivaldi-offset")||0);}'
                                  });
                              }
                          };
                          
                          setTimeout(function wait() {
                              const browser = document.getElementById('browser');
                              if (browser) {
                                  document.body.addEventListener('mousedown', tabScroll);
                              }
                              else {
                                  setTimeout(wait, 300);
                              }
                          }, 300);
                          
                          

                          I believe this is feature parity to the Opera version now. If you have more improvements, I'd be naturally interested – I've been using the initial version for months now and am a fan of this functionality. Imo all browsers should have it.

                          github ◊ vfm

                          hlehyaric 1 Reply Last reply Reply Quote 2
                          • hlehyaric
                            hlehyaric @luetage last edited by

                            @luetage I've just installed the mod, it's an interesting feature. Minimise active tab in Tabs Settings has to be disabled to make it work (just an info for anyone wanted to test it).

                            luetage 1 Reply Last reply Reply Quote 2
                            • tam710562
                              tam710562 last edited by tam710562

                              @luetage Sorry for not considering this issue 🙂
                              It seems that vivaldi's click event is pre-activated so mousedown is a good measure in this case.

                              1 Reply Last reply Reply Quote 0
                              • luetage
                                luetage @hlehyaric last edited by

                                @hlehyaric Yeah, minimise active tab changes tab when you just want to go to top or previous. Personally I never had it activated, it's far more comfortable switching tabs with shortcut. But good point.

                                github ◊ vfm

                                1 Reply Last reply Reply Quote 1
                                • luetage
                                  luetage last edited by luetage

                                  Seems like there are a few more problems I have previously overlooked…

                                  • The mousedown event triggers on right click too.
                                  • In Opera the scroll is triggered on click (mouseup left button).
                                  • When a modifier key is being used the event is triggered, but we don't intend to trigger the scroll when we want to select tabs with shift, view the context menu with ctrl, etc.
                                  • The event triggers when a tab is being moved.

                                  I tried to address all of these and it seems to behave fine. But please do test this, maybe more adjustments are needed.

                                  function tabScrollExit() {
                                      tsTarget.removeEventListener('mousemove', tabScrollExit);
                                      tsTarget.removeEventListener('click', tabScrollTrigger);
                                  };
                                  
                                  function tabScrollTrigger() {
                                      chrome.tabs.executeScript({
                                          code: 'var offset=window.pageYOffset;if(offset>0){window.sessionStorage.setItem("tabOffset",offset);window.scrollTo(0,0);}else{window.scrollTo(0,window.sessionStorage.getItem("tabOffset")||0);}'
                                      });
                                      tabScrollExit();
                                  };
                                  
                                  function tabScroll(event) {
                                      if (event.which == 1 && !event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) {
                                          tsTarget = event.target;
                                          if (tsTarget.parentNode.classList.contains('tab-header')) {
                                              tsTarget = tsTarget.parentNode;
                                          }
                                          if (tsTarget.classList.contains('tab-header') && tsTarget.parentNode.classList.contains('active')) {
                                              tsTarget.addEventListener('mousemove', tabScrollExit);
                                              tsTarget.addEventListener('click', tabScrollTrigger);
                                          }
                                      }
                                  };
                                  
                                  setTimeout(function wait() {
                                      const browser = document.getElementById('browser');
                                      if (browser) {
                                          document.body.addEventListener('mousedown', tabScroll);
                                      }
                                      else {
                                          setTimeout(wait, 300);
                                      }
                                  }, 300);
                                  
                                  

                                  github ◊ vfm

                                  1 Reply Last reply Reply Quote 1
                                  • tam710562
                                    tam710562 last edited by

                                    @luetage Thanks for this mod and your quick fixes.
                                    I found a problem, it also exists in Opera version. It is the opening of a link in the current tab of the same domain that still remembers the tab location saved by the previous tab (This doesn't affect much, but I really don't like it 🙂 ).

                                    • I added a url variable to determine if the link is saved and the current link is the same, which makes it work differently than the Opera version.
                                    • Add (function () {/ * code * /}) (); to avoid overwriting with Vivaldi functions or other mods.
                                    (function () {
                                        function tabScrollExit() {
                                            tsTarget.removeEventListener('mousemove', tabScrollExit);
                                            tsTarget.removeEventListener('click', tabScrollTrigger);
                                        }
                                    
                                        const tabScrollScript = '!' + function () {
                                            var tabOffset = JSON.parse(window.sessionStorage.getItem('tabOffset')) || {};
                                            var offset = window.pageYOffset;
                                            var urlWithoutHash = document.URL.replace(/#.*$/, '');
                                            if (offset > 0) {
                                                window.scrollTo(0, 0);
                                                tabOffset = {
                                                    offset: offset,
                                                    url: urlWithoutHash
                                                };
                                                window.sessionStorage.setItem('tabOffset', JSON.stringify(tabOffset));
                                            } else if (urlWithoutHash === tabOffset.url) {
                                                window.scrollTo(0, tabOffset.offset);
                                            }
                                        } + '();';
                                    
                                        function tabScrollTrigger() {
                                            chrome.tabs.executeScript({
                                                code: tabScrollScript
                                            });
                                            tabScrollExit();
                                        }
                                    
                                        function tabScroll(event) {
                                            if (event.which == 1 && !event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) {
                                                tsTarget = event.target;
                                                if (tsTarget.parentNode.classList.contains('tab-header')) {
                                                    tsTarget = tsTarget.parentNode;
                                                }
                                                if (tsTarget.classList.contains('tab-header') && tsTarget.parentNode.classList.contains('active')) {
                                                    tsTarget.addEventListener('mousemove', tabScrollExit);
                                                    tsTarget.addEventListener('click', tabScrollTrigger);
                                                }
                                            }
                                        }
                                    
                                        setTimeout(function wait() {
                                            const browser = document.getElementById('browser');
                                            if (browser) {
                                                document.body.addEventListener('mousedown', tabScroll);
                                            } else {
                                                setTimeout(wait, 300);
                                            }
                                        }, 300);
                                    })();
                                    

                                    I hope it will make the mod better.

                                    luetage 1 Reply Last reply Reply Quote 3
                                    • luetage
                                      luetage @tam710562 last edited by

                                      @tam710562 It definitely makes the mod better. You're fixing problems I never knew we had, which is a good thing. And apparently this versions is now superior to the Chinese Opera version, so job well done.

                                      You're good at coding modifications, I hope you stick around. By the way, although it doesn't really matter… if you do have a github account and you care for such things, please make a pull request for your improvements. I'm trying to put all my mods up on github too and you naturally deserve credit for all of these contributions.

                                      github ◊ vfm

                                      1 Reply Last reply Reply Quote 2
                                      • tam710562
                                        tam710562 last edited by

                                        @luetage Yeah, I created a pull request for my improvements.

                                        1 Reply Last reply Reply Quote 0
                                        • luetage
                                          luetage last edited by

                                          This mod ceases to function on Vivaldi version 3.7 (current snapshot). As found out by @LonM chrome.tabs.executeScript isn’t available anymore from the UI. Moreover Vivaldi switched from triggering the active tab on click, to triggering it on mousedown, which hurts this mod too. I don’t see what to do about either of these issues at this point in time.

                                          Workaround: Switch to Opera browser immediately.

                                          GMO I’m joking you guys, relax. Vimium provides the functionality to set local marks on webpages, which allows to jump to top and back too. Moreover we can set more than one mark, so it’s actually more advanced, just keyboard driven.

                                          github ◊ vfm

                                          luetage 1 Reply Last reply Reply Quote 3
                                          • luetage
                                            luetage @luetage last edited by

                                            @luetage Thanks to @tam710562 this mod works again, see here ☛ https://forum.vivaldi.net/topic/57191/restore-methods-for-chrome-tabs. You will have to insert the code in your modfile, then executing scripts will work as intended on webpages. Additionally I rewrote the mod to work with 2 level tab stacks and apparently we lucked out: The mousedown change implemented by Vivaldi in the latest version doesn’t influence this mod. It seems like the mousedown listener in this script is faster than the switching of tabs in Vivaldi. To make a short story longer: all functionality is restored and the mod should work as expected.

                                            github ◊ vfm

                                            1 Reply Last reply Reply Quote 3
                                            Loading More Posts
                                            • Oldest to Newest
                                            • Newest to Oldest
                                            • Most Votes
                                            • Reply as topic
                                            Log in to reply
                                            • 1
                                            • 2
                                            • 3
                                            • 1 / 3
                                            • First post
                                              Last post

                                            Looks like your connection to Vivaldi Forum was lost, please wait while we try to reconnect.

                                            Copyright © Vivaldi Technologies™ — All rights reserved. Privacy Policy | Code of conduct | Terms of use | Vivaldi Status