How To Use Mutation Observer In SPFx (Application Customizer)

The following blog will help you to add your custom action menu in SharePoint modern page. As we are not able to get the DOM element for action menu in OnInit() method of SPFx(application customizer) so here we will see how mutation observer helps to get the DOM element SharePoint modern pages custom action menus and add your own custom action menu to the existing menus.

  1. To use mutation observer in your client-side extension, first, you need to install mutation observer in your solution using the command “npm install MutationObserver-shim”.

    How To Use Mutation Observer In SPFx

  2. Then write the comment “require(‘mutationobserver-shim’)” to include mutation observer operations in client-side extensions. 

    How To Use Mutation Observer In SPFx

  3. Update HelloWorldApplicationCustomizer class codes as provided.

Requirement

Our requirement was to add our custom personal action menu where we wanted to execute custom JavaScript functions. In oninit method, if we try to get the menus to edit the existing one to add our custom menus then we will not be able to get those default action menus as those menus are not getting rendered in page onload.

How To Use Mutation Observer In SPFx 
 
For this, we need to use MutationObserver to add our custom menu. Using the following code, I successfully added my custom action menus.
 
How To Use Mutation Observer In SPFx 
 
Here mentioned below is the complete typescript file which contains all codes based on the requirement mentioned in this blog,
  1. import { override } from '@microsoft/decorators';  
  2. import { Log } from '@microsoft/sp-core-library';  
  3. import {  
  4. BaseApplicationCustomizer  
  5. } from '@microsoft/sp-application-base';  
  6. import { Dialog } from '@microsoft/sp-dialog';  
  7. require('mutationobserver-shim');  
  8. import * as strings from 'HelloWorldApplicationCustomizerStrings';  
  9. const LOG_SOURCE: string = 'HelloWorldApplicationCustomizer';  
  10. /** 
  11. * If your command set uses the ClientSideComponentProperties JSON input, 
  12. * it will be deserialized into the BaseExtension.properties object. 
  13. * You can define an interface to describe it. 
  14. */  
  15. export interface IHelloWorldApplicationCustomizerProperties {  
  16. // This is an example; replace with your own property  
  17. testMessage: string;  
  18. }  
  19. let _observer: MutationObserver;  
  20. /** A Custom Action which can be run during execution of a Client Side Application */  
  21. export default class HelloWorldApplicationCustomizer  
  22. extends BaseApplicationCustomizer<IHelloWorldApplicationCustomizerProperties> {  
  23. @override  
  24. public onInit(): Promise < void > {  
  25.     Log.info(LOG_SOURCE, `Initialized ${strings.Title}`);  
  26.     let message: string = this.properties.testMessage;  
  27.     if (!message) {  
  28.         message = '(No properties were provided.)';  
  29.     }  
  30.     this.context.placeholderProvider.changedEvent.add(thisthis.renderObserver);  
  31.     return Promise.resolve();  
  32. }  
  33. private async renderObserver(): Promise < void > {  
  34.     if (!_observer) {  
  35.         // Register observable  
  36.         let config = {  
  37.             childList: true,  
  38.             subtree: true  
  39.         };  
  40.         _observer = new MutationObserver(this.addMyCustomMenu);  
  41.         _observer.observe(document, config);  
  42.     }  
  43. }  
  44. private addMyCustomMenu(mutationsList) {  
  45.     var o365Menus = document.querySelectorAll("*[class^=\"o365cs-mfp-linklist o365cs-segoeRegular o365cs-text-align-left\"]");  
  46.     if (o365Menus.length > 0) {  
  47.         for (var i in o365Menus) {  
  48.             try {  
  49.                 var menuItem = o365Menus[i];  
  50.                 var innerhtml = menuItem.innerHTML;  
  51.                 var newhtml = '<div class="ms-fcl-tp o365cs-nfd-normal-lineheight"><a class="ms-fcl-tp o365button" role="link"';  
  52.                 newhtml += 'href="javascript:alert(\"Hello\");" id="O365_SubLink_CustomMenu"';  
  53.                 newhtml += 'aria-labelledby="_ariaId_9"><span class="_fc_3 owaimg" style="display: none;"></span>';  
  54.                 newhtml += '<span class="_fc_4 o365buttonLabel" id="_ariaId_9">My Custom Menu</span></a></div>';  
  55.                 menuItem.innerHTML = innerhtml.concat(newhtml);  
  56.             } catch (e) {}  
  57.         }  
  58.     }  
  59. }  
  60. private _onDispose(): void {  
  61.     if (_observer) {  
  62.         _observer.disconnect();  
  63.     }  
  64.     console.log('[HelloWorldApplicationCustomizer._onDispose] Disposed custom.');  
  65.    }  
  66. }