I am following the Microsoft tutorial for creating an Application Customizer. That works great, but now I would like to add some custom things like a curtain menu to my site.
I can't seem to figure out how to add an event listener to an element that is being rendered from a promise. The element doesn't exist initially, so if I try the ID, I get a null error. If I use a class it gives me an inline script error.
I've searched for a solution, but don't see one that applies to what I am doing.
I know this code is a mess, but I've been trying so many different methods, I can't seem to find one that works.
import { override } from '@microsoft/decorators';
import { Log } from '@microsoft/sp-core-library';
import {
BaseApplicationCustomizer,
PlaceholderContent,
PlaceholderName,
PlaceholderProvider
} from '@microsoft/sp-application-base';
import { Dialog } from '@microsoft/sp-dialog';
import * as strings from 'HideSideNavApplicationCustomizerStrings';
// import * as strings from './myStrings';
import styles from './AppCustomizer.module.scss';
import {escape} from '@microsoft/sp-lodash-subset';
import Placeholder from '@microsoft/sp-application-base/lib/extensibility/placeholder/Placeholder';
const LOG_SOURCE: string = 'HideSideNavApplicationCustomizer';
let openBtn = "openBtn";
export interface IHideSideNavApplicationCustomizerProperties {
// This is an example; replace with your own property
testMessage: string;
Top: string;
openBtn: string;
}
/** A Custom Action which can be run during execution of a Client Side Application */
export default class HideSideNavApplicationCustomizer
extends BaseApplicationCustomizer<IHideSideNavApplicationCustomizerProperties> {
private _topPlaceholder: PlaceholderContent | undefined;
@override
public onInit(): Promise<void> {
this.context.placeholderProvider.changedEvent.add(this, this._renderPlaceHolders);
return Promise.resolve();
}
private _renderPlaceHolders(): void {
console.log('calling _renderPlaceHolders');
console.log(
"Available placeholders: ",
this.context.placeholderProvider.placeholderNames
.map(name => PlaceholderName[name])
.join(", ")
);
if(!this._topPlaceholder){
this._topPlaceholder = this.context.placeholderProvider.tryCreateContent(
PlaceholderName.Top,
{ onDispose: this.onDispose}
)
}
if(!this._topPlaceholder) {
console.error("The expected placeholder (Top) was not found.");
return;
}
if(this.properties){
let topString: string = `
<!-- The overlay -->
<div id="myNav" class="navClose overlay">
<!-- Button to close the overlay navigation -->
<a href="javascript:void(0)" class="closebtn" onclick="closeNav()">×</a>
<!-- Overlay content -->
<div class="overlay-content">
<a href="#">About</a>
<a href="#">Services</a>
<a href="#">Clients</a>
<a href="#">Contact</a>
</div>
</div>
<!-- Use any element to open/show the overlay navigation menu -->
<span class="navOpen">open</span>
`;
if(!topString){
topString = "(Top property was not defined.)";
}
if(this._topPlaceholder.domElement){
this._topPlaceholder.domElement.innerHTML=topString;
let navState :string = "closed";
const navClose = document.getElementsByClassName("navClose").item(0);
this._topPlaceholder.domElement.addEventListener("click", function(e){
if(navState == "closed"){
navClose.setAttribute(this.style.width, "100%");
navState = "opened";
}
else{
navClose.setAttribute(this.style.width, "0");
navState = "closed";
}
});
}
}
}
private _onDispose(): void {
console.log('[HideSideNavApplicationCustomizer._onDispose] Dispose custom top and bottom.')
}
}
So I think I finally figured this out. I wasn't using 'this' correctly after the promise was made, and there were a number of syntax errors with how I was trying to update the attributes on the elements.
Here's my updated code in case anyone is trying to do something similar.
import { override } from '@microsoft/decorators';
import { Log } from '@microsoft/sp-core-library';
import {
BaseApplicationCustomizer,
PlaceholderContent,
PlaceholderName,
PlaceholderProvider
} from '@microsoft/sp-application-base';
import styles from './AppCustomizer.module.scss';
const LOG_SOURCE: string = 'HideSideNavApplicationCustomizer';
let openBtn = "openBtn";
export interface IHideSideNavApplicationCustomizerProperties {
// This is an example; replace with your own property
testMessage: string;
Top: string;
}
/** A Custom Action which can be run during execution of a Client Side Application */
export default class HideSideNavApplicationCustomizer
extends BaseApplicationCustomizer<IHideSideNavApplicationCustomizerProperties> {
private _topPlaceholder: PlaceholderContent | undefined;
@override
public onInit(): Promise<void> {
this.context.placeholderProvider.changedEvent.add(this, this._renderPlaceHolders);
return Promise.resolve();
}
private _renderPlaceHolders(): void {
console.log('calling _renderPlaceHolders');
console.log(
"Available placeholders: ",
this.context.placeholderProvider.placeholderNames
.map(name => PlaceholderName[name])
.join(", ")
);
if(!this._topPlaceholder){
this._topPlaceholder = this.context.placeholderProvider.tryCreateContent(
PlaceholderName.Top,
{ onDispose: this.onDispose}
)
}
if(!this._topPlaceholder) {
console.error("The expected placeholder (Top) was not found.");
return;
}
if(this.properties){
let topString: string = `
<!-- The overlay -->
<div id="nav" class="${styles.overlay}">
<!-- Button to close the overlay navigation -->
<a href="javascript:void(0)" id="navClose" class="${styles.closebtn}">×</a>
<!-- Overlay content -->
<div class="${styles['overlay-content']}">
<a href="#">About</a>
<a href="#">Services</a>
<a href="#">Clients</a>
<a href="#">Contact</a>
</div>
</div>
<!-- Use any element to open/show the overlay navigation menu -->
<span id="navOpen">open</span>
`;
if(!topString){
topString = "(Top property was not defined.)";
}
if(this._topPlaceholder.domElement){
const top = this._topPlaceholder.domElement;
top.innerHTML=topString;
let nav = top.querySelector('#nav');
let navOpen = top.querySelector('#navOpen');
let navClose = top.querySelector('#navClose');
navOpen.addEventListener("click", () => {
nav.setAttribute("style","width:75%;");
});
navClose.addEventListener("click", () => {
nav.setAttribute("style","width:0%;");
});
}
}
}
private _onDispose(): void {
console.log('[HideSideNavApplicationCustomizer._onDispose] Dispose custom top and bottom.')
}
}