Search code examples
javascripthtmldomgoogle-chrome-extensioncontent-script

Prevent scroll in Google Calendar after appending DIV via Chrome Extension and creating/canceling new calendar event


I'm writing an Chrome Extension for Google Calendar, which adds an div under the header section of Google Calendar.

For simplicity, this is what essentially happens in Chrome Extension content script, you can just paste this code in the console of calendar.google.com to check what happens:

let header = document.querySelector("header[role='banner']");
let appContainer = document.createElement('DIV');
appContainer.style.height = '200px';
appContainer.style.backgroundColor = 'red';
header.parentNode.insertBefore(appContainer, header.nextSibling);

The problem I experience is, after creating or canceling the creation of an event in calendar view, the window scrolls up and doesn't show the page header now.

Anyone have an idea how to keep page steady after creating or canceling of an event in calendar view, and keep the div I append via Chrome Extension content script too?

EDIT: Here are screenshots of how it looks like before the event creation/cancel and after:

before

enter image description here


Solution

  • Normally, without your extension, header.parentElement contains two elements that count towards header.parentElement.clientHeight: header, and the div that contains the calendar itself:

    enter image description here

    (It also contains a few floating buttons and hidden divs and stuff, but those aren't important here.)

    These two elements, header and calendar, have heights that are calculated specifically so that header.clientHeight + calendar.clientHeight is equal to the height of the page. Under normal circumstances this is perfect since it means there's no need for scrolling.

    However, in your case, that you add an extra div, which pushes calendar down:

    enter image description here

    Normally you would be able to scroll down yourself to see the bottom of calendar, but since the scroll bar is disabled, you can't. However, when you create an event, your browser sees that you are trying to access the bottom of calendar, so it scrolls down automatically to show you the bottom of calendar. Since the whole page is now scrolled down to make the bottom of the page visible, the top of the page is now invisible, resulting in the behavior that you describe.

    The way to fix this is to make adjust the height of calendar so that header.clientHeight + appContainer.clientHeight + calendar.clientHeight is equal to the height of the page, rather than just header.clientHeight + calendar.clientHeight. This can be done by adding the following code:

    //Getting calendar can be a bit tricky since it's just one dive among others.
    //While it does have a CSS class, I don't know how that name is determined or if it will change in future versions of Google Calendar, so it's best not to use it.
    //What I do here instead is that I select the first child of header.parentElement that has parts outside of the page frame
    const getCalendar = () => [...header.parentElement.querySelectorAll(":scope > div")].find(d => {
        let boundingRect = d.getBoundingClientRect();
        return boundingRect.y + boundingRect.height > document.body.clientHeight;
    });
    let calendar = getCalendar();
    
    //This function adjusts the height of calendar
    const adjustHeight = () => {
        calendar = calendar || getCalendar();    //calendar may be null in the beginning before all the elements were set, so if it's null try to select it again
        if(calendar != null){
            calendar.style.height = (calendar.parentElement.clientHeight - header.clientHeight - appContainer.clientHeight) + "px";    //Adjust the height of calendar
        }
    };
    
    window.addEventListener("resize", adjustHeight);    //Adjust the height whenever the window gets resized
    window.addEventListener("load", adjustHeight);    //Adjust the height on page load