Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Scroll manager is a class that help with saving the scroll positing when you
18
 * click on an action icon, and then when the page is reloaded after processing
19
 * the action, it scrolls you to exactly where you were. This is much nicer for
20
 * the user.
21
 *
22
 * To use this in your code, you need to ensure that:
23
 * 1. The button that triggers the action has to have a click event handler that
24
 *    calls saveScrollPos()
25
 * 2. After doing the processing, the redirect() function will add 'mdlscrollto'
26
 *    parameter into the redirect url automatically.
27
 * 3. Finally, on the page that is reloaded (which should be the same as the one
28
 *    the user started on) you need to call scrollToSavedPosition()
29
 *    on page load.
30
 *
31
 * @module     core/scroll_manager
32
 * @copyright  2021 The Open University
33
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 */
35
 
36
/** @property {HTMLElement} scrollingElement the current scrolling element. */
37
let scrollingElement = null;
38
 
39
/**
40
 * Is the element scrollable?
41
 *
42
 * @param {HTMLElement} element Element.
43
 * @returns {boolean}
44
 */
45
const isScrollable = (element) => {
46
    // Check if the element has scrollable content.
47
    const hasScrollableContent = element.scrollHeight > element.clientHeight;
48
 
49
    // If 'overflow-y' is set to hidden, the scroll bar is't show.
50
    const elementOverflow = window.getComputedStyle(element).overflowY;
51
    const isOverflowHidden = elementOverflow.indexOf('hidden') !== -1;
52
 
53
    return hasScrollableContent && !isOverflowHidden;
54
};
55
 
56
/**
57
 * Get the scrolling element.
58
 *
59
 * @returns {HTMLElement}
60
 */
61
const getScrollingElement = () => {
62
    if (scrollingElement === null) {
63
        const page = document.getElementById('page');
64
        if (isScrollable(page)) {
65
            scrollingElement = page;
66
        } else {
67
            scrollingElement = document.scrollingElement;
68
        }
69
    }
70
 
71
    return scrollingElement;
72
};
73
 
74
/**
75
 * Get current scroll position.
76
 *
77
 * @returns {Number} Scroll position.
78
 */
79
const getScrollPos = () => {
80
    const scrollingElement = getScrollingElement();
81
 
82
    return scrollingElement.scrollTop;
83
};
84
 
85
/**
86
 * Get the scroll position for this form.
87
 *
88
 * @param {HTMLFormElement} form
89
 * @returns {HTMLInputElement}
90
 */
91
const getScrollPositionElement = (form) => {
92
    const element = form.querySelector('input[name=mdlscrollto]');
93
    if (element) {
94
        return element;
95
    }
96
 
97
    const scrollPos = document.createElement('input');
98
    scrollPos.type = 'hidden';
99
    scrollPos.name = 'mdlscrollto';
100
    form.appendChild(scrollPos);
101
 
102
    return scrollPos;
103
};
104
 
105
/**
106
 * In the form that contains the element, set the value of the form field with
107
 * name mdlscrollto to the current scroll position. If there is no element with
108
 * that name, it creates a hidden form field with that name within the form.
109
 *
110
 * @param {string} elementId The element in the form.
111
 */
112
export const saveScrollPos = (elementId) => {
113
    const element = document.getElementById(elementId);
114
    const form = element.closest('form');
115
    if (!form) {
116
        return;
117
    }
118
 
119
    saveScrollPositionToForm(form);
120
};
121
 
122
/**
123
 * Init event handlers for all links with data-savescrollposition=true.
124
 * Set the value to the closest form.
125
 */
126
export const watchScrollButtonSaves = () => {
127
    document.addEventListener('click', (e) => {
128
        const button = e.target.closest('[data-savescrollposition="true"]');
129
        if (button) {
130
            saveScrollPositionToForm(button.form);
131
        }
132
    });
133
};
134
 
135
/**
136
 * Save the position to form.
137
 *
138
 * @param {Object} form The form is saved scroll position.
139
 */
140
export const saveScrollPositionToForm = (form) => {
141
    getScrollPositionElement(form).value = getScrollPos();
142
};
143
 
144
/**
145
 * Init event handlers for all links with data-save-scroll=true.
146
 * Handle to add mdlscrollto parameter to link using js when we click on the link.
147
 *
148
 */
149
export const initLinksScrollPos = () => {
150
    document.addEventListener('click', (e) => {
151
        const link = e.target.closest('a[data-save-scroll=true]');
152
        if (!link) {
153
            return;
154
        }
155
 
156
        e.preventDefault();
157
        const url = new URL(e.target.href);
158
        url.searchParams.set('mdlscrollto', getScrollPos());
159
        window.location = url;
160
    });
161
};
162
 
163
/**
164
 * If there is a parameter like mdlscrollto=123 in the URL, scroll to that saved position.
165
 */
166
export const scrollToSavedPosition = () => {
167
    const url = new URL(window.location.href);
168
    if (!url.searchParams.has('mdlscrollto')) {
169
        return;
170
    }
171
 
172
    const scrollPosition = url.searchParams.get('mdlscrollto');
173
 
174
    // Event onDOMReady is the effective one here. I am leaving the immediate call to
175
    // window.scrollTo in case it reduces flicker.
176
    const scrollingElement = getScrollingElement();
177
    scrollingElement.scrollTo(0, scrollPosition);
178
    document.addEventListener('DOMContentLoaded', () => {
179
        scrollingElement.scrollTo(0, scrollPosition);
180
    });
181
};