API Docs for:
Show:

File: development/modules/anchor/js/anchor.js

import $ from 'jquery';
import {last} from '../../../js/utils/array';
import DOMModule from '../../__base/dommodule';

/**
 * @module Modules
 */
const DEFAULT_CONTEXT = 'html, body';
const DEFAULT_OFFSET = 0;
const DEFAULT_DURATION = 300;
const DEFAULT_URL_EXCLUSION = true;

const AnchorTriggerStates = [
    {name: 'init', events: {render: 'rendered'}},
    {name: 'rendered', events: {start: 'running'}},
    {name: 'running', events: {}}
];

// PRIVATE METHODS
const handleClick = Symbol('_handleClick');



/**
 * AnchorTrigger module
 * @class Modules.AnchorTrigger
 * @constructor
 * @extends Modules._DOMModule
 */
class AnchorTrigger extends DOMModule {
    constructor (options) {
        super(options);

        this.addStates(AnchorTriggerStates);

        this._offset = $(this.$data('excluded-height')).outerHeight() || DEFAULT_OFFSET;
        this._duration = this.$data('duration') || DEFAULT_DURATION;
        this._hideFromURL = this.$data('anchor-url') || DEFAULT_URL_EXCLUSION;
        this._reference = last(this.$rootNode[0].href.split('#'));

        this.$contextNode = $(this.$data('context') || DEFAULT_CONTEXT);
        this.$referenceNode = $('#' + this._reference);
    }
    /**
     * Creates a new AnchorTrigger instance without the "new" keyword but does not
     *     initialize it
     * @method of
     * @static
     * @for Modules.AnchorTrigger
     * @param {node|string|configs} node Node, CSS selector or configuration object
     * @return {AnchorTrigger} A new instance
     * 
     * @example
     *     import AnchorTrigger from './modules/anchor/js/anchor';
     *
     *     const myTrigger = AnchorTrigger.of('[data-widget=AnchorTrigger]');
     *     myTrigger.render().startUp();
     */
    static of (node) {
        return new AnchorTrigger(node);
    }
    /**
     * Finds and initializes all Accordions on a page at once
     * @method findAll
     * @static
     * @for Modules.AnchorTrigger
     * @param {string} selector CSS selector to match nodes against
     * @return {array} A list of instances
     * 
     * @example
     *     import AnchorTrigger from './modules/anchor/js/anchor';
     *
     *     AnchorTrigger.findAll('[data-widget=AnchorTrigger]');
     */
    static findAll(selector) {
        return $(selector).toArray().map((node) => {
            const trigger = AnchorTrigger.of(node);
            trigger.render().startUp();
            return trigger;
        });
    }
    /**
     * Renders the instance and checks if a matching reference node has been found
     * @method render
     * @for Modules.AnchorTrigger
     * @return {this} The instance
     * 
     * @example
     *     import AnchorTrigger from './modules/anchor/js/anchor';
     *
     *     const myTrigger = AnchorTrigger.of('[data-widget=AnchorTrigger]');
     *     myTrigger.render();
     */
    render() {
        if (this.getState() === 'init') {
            this.consume('render');

            if (!this.$referenceNode.length) {
                throw `AnchorTrigger: No matching anchor found: #${this._reference}`;
            }
        }
        return this;
    }
    /**
     * Attaches all eventlisteners to the instance
     * @method startUp
     * @for Modules.AnchorTrigger
     * @return {undefined} Nothing
     * 
     * @example
     *     import AnchorTrigger from './modules/anchor/js/anchor';
     *
     *     const myTrigger = AnchorTrigger.of('[data-widget=AnchorTrigger]');
     *     myTrigger.render().startUp();
     */
    startUp() {
        if (this.getState() === 'rendered') {
            this.consume('start');
            this.onClick(handleClick);
        }
    }
    // === PRIVATE STUFF ===
    /**
     * Updates the hash part of the current URL of the browser
     * @method _updateHash
     * @for Modules.AnchorTrigger
     * @private
     * @param {string} hash The new location.hash value
     * @return {undefined} Nothing
     */
    _updateHash (_hash) {
        location.hash = _hash;
    }
    // === EVENT HANDLERS ===
    [handleClick](event) {
        event.preventDefault();

        this.$contextNode.scrollTo(
            this.$referenceNode,
            this._duration,
            {offset: -this._offset ,axis: 'y'}
        );
        if (!this._hideFromURL) {
            this._updateHash(this._reference);
        }
    }
}



export default AnchorTrigger;