0

I am trying to create a generic component to stick the header in LWC.

Here is my component.

HTML

<template>
    <div id="stickyId" class="myStickyHeader">
        <slot>
            <!-- Sticky content goes here -->
        </slot>
    </div>
</template>

I have defined sticky2 to class for a sticky header.

Js controller

/* eslint-disable no-console */
import { LightningElement } from 'lwc';

export default class Sticky extends LightningElement {
    renderedCallback() {
        try {
            window.onscroll = () => {
                let stickysection = this.template.querySelector('.myStickyHeader');
                let sticky2 = stickysection.offsetTop;
                console.log('stickysection  => ' + stickysection);
                console.log('stickysection id  => ' + stickysection.id);
                console.log('sticky2  => ' + sticky2);
                console.log('window.pageYOffset  => ' + window.pageYOffset);
                if (window.pageYOffset > sticky2) {
                    stickysection.classList.add("sticky2");
                } else {
                    stickysection.classList.remove("sticky2");
                }
            }
        } catch (error) {
            console.log('error =>', error);
        }
    }
}

I am using this component in another component like.

<c-sticky> 
    <div> my header section </div>
</c-sticky>

This works well if I have only one instance of sticky component on my lightning page with tabs. But when there are multiple instances, it is not applying the sticky2 class on the currenly focused header.

See the image below, I am want to stick the header section of the component in each tab. So when I am in the Details tab, its actully applying sticky2 class to header in Design tab.

enter image description here

I think there are two problems here.

  • Onscroll method is not able to detect which component is currently focused.
  • Tried querySelectorAll to get all of the 'divs' on the page. But could not get them.

2 Answers 2

2

The way you select an element is this.template.querySelector

ie you can only select the elements which are created by you in current component. Encapsulation by locker lwc. You would never be able to access other elements on the page from your component, imagine a rogue app exchange component that can read other markup data.

Thus querySelectorAll wont work ever.

A simple solution I can think of is adding an extra attribute inside your sticky component that determines when to show or hide it.

<template>
    <template if:true={show}>
        <div id="stickyId" class="myStickyHeader">
            <slot>
                <!-- Sticky content goes here -->
            </slot>
        </div>
    </template>    
</template>

JS:

/* eslint-disable no-console */
import { LightningElement,api } from 'lwc';

export default class Sticky extends LightningElement {

    @api show = true;

    renderedCallback() {
        try {
            window.onscroll = () => {
                let stickysection = this.template.querySelector('.myStickyHeader');
                let sticky2 = stickysection.offsetTop;
                console.log('stickysection  => ' + stickysection);
                console.log('stickysection id  => ' + stickysection.id);
                console.log('sticky2  => ' + sticky2);
                console.log('window.pageYOffset  => ' + window.pageYOffset);
                if (window.pageYOffset > sticky2) {
                    stickysection.classList.add("sticky2");
                } else {
                    stickysection.classList.remove("sticky2");
                }
            }
        } catch (error) {
            console.log('error =>', error);
        }
    }
}

And then show sticky when the current tab is active.

<c-sticky show={isCurrentTabActive}> 
    <div> my header section </div>
</c-sticky>

Anyways... I am not a big fan of javascript to handle positions. What you can do is just use SLDS slds-is-fixed

<template>
    <div id="stickyId" class="slds-is-fixed">
        <slot>
            <!-- Sticky content goes here -->
        </slot>
    </div>
</template>

No need of JS scrolling workaround.

7
  • How can I set isCurrentTabActive? As far as I know onselect don't work in LWC Oct 15, 2019 at 12:47
  • what kinda tab it is? Standard SF tabs from app builder? Oct 15, 2019 at 12:48
  • I am using both tabs, Standard App builder tabs, also lightning-tab for subtabs Oct 15, 2019 at 12:51
  • @rahulgawale can y ou try slds-is-fixed for now and see if it works for you? Oct 15, 2019 at 12:54
  • 1
    Actually, I got why it's not working, I am setting window.onscroll method in renderedcallback of the component, which gets overwritten when the new component is rendered, so when I switch back to the component which is rendered previously, I don't work as window.onscroll is referring to the most recent rendered component. Oct 15, 2019 at 14:24
1

Actually, Solved my problem with another approach, even I was able to stick the header to the top of the window, I was very difficult to manage the position on various devices, later I got very simple thought in my mind, I wonder why did not I get it earlier.

Firstly, I fixed the headers in each of the tabs, then I put the rest of the things in a scrollable div using slds-scrollable_y. To enable scrolling we need to have a fixed height, I used media queries to manage height based on device form factor.

So here is the sample code.

<div class="fixedHead">
    Header content here!
</div>
<div class="fixedHeithBasedOnFormFactor slds-scrollable_y">
    Content here!
</div>

So the user will be able to scroll fixedHeithBasedOnFormFactor slds-scrollable_y content only and the header is fixed and page's scroller is disabled as well

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .