QuickAnswer
by

JavaScript multifunctional smooth scrolling SVG support

JavaScript multifunctional smooth scrolling SVG support

I've been writing JavaScript for smooth scrolling for a long time, but I've rewritten it to organize it.
The code is long, but it's the result of extending it to handle various situations.
Of course, it doesn't use jQuery. It works by itself.
The features and functions are as follows:

  • It works just by installing it alone.
  • Scrollable to anchor tags in inline SVG.
  • Dynamic anchor tag placement is also supported.
  • Scrolling is possible by directly specifying the anchor tag.
  • Directly specify an ID name to scroll.
  • Direct scrolling by specifying a numerical value for the scroll position.
  • Directly specify an element to scroll.
  • Scroll speed can be set.
  • Scroll position can be adjusted. If the header is 100px long, it can be moved by 100px.
  • etc.

It will continue to expand with the times.

/**
 * スムーススクロール インラインSVG内アンカータグ対応
 *
 * usage:
 * <script src="smoothscroll.js"></script> //このファイル
 * 以下、必要に応じて:
 * <script> SmoothScroll.setOption({offset:-200}); </script> //オフセット位置設定(スクロール位置をずらす)
 * <script> SmoothScroll.setOption({speed:20}); </script> //速度設定
 * <script> SmoothScroll.reInit(); </script> //動的に要素を追加したときに再スキャン
 * <script> SmoothScroll.addRender(elm); </script> //aタグを動的に追加した場合にイベントリスナー追加
 * <script> SmoothScroll.elementScroll(elm); </script> //エレメントへスクロール
 * <script> SmoothScroll.anchorScroll('anchorName'); </script> //ページ読み込み後にアンカー名へスクロール
 * <script> SmoothScroll.nameScroll('anchorName'); </script> //アンカー名へスクロール
 * <script> SmoothScroll.idScroll('idName'); </script> //id名へスクロール
 * <script> SmoothScroll.positionScroll(800); </script> //数値指定でスクロール
 *
 * @author ao-system, Inc.
 *
 * @date 2015.04.26
 * @date 2017.07.25
 * @date 2017.09.22
 * @date 2017.10.03
 * @date 2018.06.20
 * @date 2018.09.11
 * @date 2019.07.18
 * @date 2021.01.19
 * @date 2022.01.06
 */

'use strict';
const SmoothScroll = {

    _speed: 10,
    _offset: 0,
    _lastPos: 0,
    _interval: 0,
    _direction: '',

    //要素の位置を返す
    offsettop: (elm) => {
        let offsettop = elm.offsetTop;
        if (elm.offsetParent) {
            while (elm = elm.offsetParent) {
                offsettop += elm.offsetTop;
            }
        }
        return Math.max(0, offsettop + SmoothScroll._offset);
    },
    //スクロール位置を返す
    scrolltop: () => {
        if (document.body && document.body.scrollTop) {
            return document.body.scrollTop;
        }
        if (document.documentElement && document.documentElement.scrollTop) {
            return document.documentElement.scrollTop;
        }
        if (window.pageYOffset) {
            return window.pageYOffset;
        }
        return 0;
    },
    //イベントをキャンセルする
    killEvt: (evt) => {
        if (evt.preventDefault && evt.stopPropagation) {
            evt.preventDefault();
            evt.stopPropagation();
        } else {
            evt.cancelBubble = true;
            evt.returnValue = false;
        }
    },
    //目的地へスクロール
    positionScroll: (targetPos) => {
        let currentPos = SmoothScroll.scrolltop();    //現在の位置
        if (SmoothScroll._direction == '') {    //初回に移動方向を記録
            SmoothScroll._direction = targetPos > currentPos ? 'plus' : 'minus';
        }
        let moveSize = Math.ceil((targetPos - currentPos) / SmoothScroll._speed);
        if (moveSize == 0) {    //目的地まで達する様に調整
            moveSize = targetPos > currentPos ? 1 : -1;
        }
        currentPos += moveSize;
        window.scrollTo(0,currentPos);
        if (currentPos == targetPos || SmoothScroll._lastPos == currentPos) {    //目的地に達したか、動けなくなったか
            clearInterval(SmoothScroll._interval);
            SmoothScroll._direction = '';
            window.scrollTo(0,targetPos);
        }
        if ((SmoothScroll._direction == 'plus' && targetPos < currentPos) || (SmoothScroll._direction == 'minus' && targetPos > currentPos)) {    //移動方向に矛盾が生じた場合
            clearInterval(SmoothScroll._interval);
            SmoothScroll._direction = '';
            window.scrollTo(0,targetPos);
        }
        SmoothScroll._lastPos = currentPos;
    },
    //クリック時のイベント
    renderClick: (evt) => {
        SmoothScroll.killEvt(evt);
        let href = evt.target.href || evt.target.parentNode.href || evt.target.parentNode.parentNode.href || evt.target.parentNode.parentNode.parentNode.href || evt.target.parentNode.parentNode.parentNode.parentNode.href || null;
        let hash = evt.target.hash || evt.target.parentNode.hash || evt.target.parentNode.parentNode.hash || evt.target.parentNode.parentNode.parentNode.hash || evt.target.parentNode.parentNode.parentNode.parentNode.hash || null;
        let anchorTagName = '';
        if (href && href.baseVal) {    //SVGの場合 e.g. <svg><a href="#footer"><rect .. /></a></svg>
            anchorTagName = href.baseVal.substr(1);
        } else if (hash) {
            anchorTagName = hash.substr(1);
        } else {
            return;
        }
        const anchorTags = document.getElementsByTagName('a');
        Array.prototype.forEach.call(anchorTags,(anchorTag) => {
            if (anchorTag.name == anchorTagName) {
                clearInterval(SmoothScroll._interval);
                ((anchorTag) => {
                    SmoothScroll.elementScroll(anchorTag);
                })(anchorTag);
            }
        });
    },
    //イベントリスナー用意
    render: () => {
        const anchorTags = document.getElementsByTagName('a');
        Array.prototype.forEach.call(anchorTags,(anchorTag) => {
            if (anchorTag.href) {
                if (anchorTag.href.baseVal) {    //SVGの場合 e.g. <svg><a href="#footer"><rect .. /></a></svg>
                    if (anchorTag.href.baseVal.substr(0,1) == '#') {
                        ((elm) => {
                            elm.removeEventListener('click',SmoothScroll.renderClick);
                            elm.addEventListener('click',SmoothScroll.renderClick);
                        })(anchorTag);
                    }
                } else {
                    if (anchorTag.href.indexOf('#') != -1 && ((anchorTag.pathname == location.pathname) || ('/' + anchorTag.pathname == location.pathname))) {
                        ((elm) => {
                            elm.removeEventListener('click',SmoothScroll.renderClick);
                            elm.addEventListener('click',SmoothScroll.renderClick);
                        })(anchorTag);
                    }
                }
            }
        });
    },
    //オプション設定
    setOption: (obj) => {
        if (obj.offset) {
            SmoothScroll._offset = obj.offset;    //スクロール位置のオフセット
        }
        if (obj.speed) {
            SmoothScroll._speed = obj.speed;    //速度設定
        }
    },
    //初期設定
    init: () => {
        window.addEventListener('load',SmoothScroll.render);
    },
    //初期設定 再度
    reInit: () => {
        SmoothScroll.render();
    },
    //イベントリスナー 後から個別に追加
    addRender: (anchorElm) => {
        if (anchorElm.href) {
            if (anchorElm.href.baseVal) {    //SVGの場合 e.g. <svg><a href="#footer"><rect .. /></a></svg>
                if (anchorElm.href.baseVal.substr(0,1) == '#') {
                    anchorElm.removeEventListener('click',SmoothScroll.renderClick);
                    anchorElm.addEventListener('click',SmoothScroll.renderClick);
                }
            } else {
                if (anchorElm.href.indexOf('#') != -1 && ((anchorElm.pathname == location.pathname) || ('/' + anchorElm.pathname == location.pathname))) {
                    anchorElm.removeEventListener('click',SmoothScroll.renderClick);
                    anchorElm.addEventListener('click',SmoothScroll.renderClick);
                }
            }
        }
    },
    //elementへスクロール
    elementScroll: (elm) => {
        if (elm) {
            SmoothScroll._interval = setInterval(() => {SmoothScroll.positionScroll(SmoothScroll.offsettop(elm))},10);
        }
    },
    //アンカー名へスクロールする Load時にセット
    anchorScroll: (nameValue) => {
        window.addEventListener('load',() => {
            if (document.getElementById('loading')) {    //ページロード中表示があればそれを消す
                document.getElementById('loading').outerHTML = '';
            }
            SmoothScroll.elementScroll(document.getElementsByName(nameValue).item(0));
        });
    },
    //name名へスクロールする。ダイレクト実行
    nameScroll: (nameValue) => {
        SmoothScroll.elementScroll(document.getElementsByName(nameValue).item(0));
    },
    //ID名へスクロールする。ダイレクト実行
    idScroll: (idValue) => {
        SmoothScroll.elementScroll(document.getElementById(idValue));
    },
};
SmoothScroll.init();
CONTENTS
Web Browser