Place SPFx Webpart Inside A DIV Tag On A Page

Introduction

 
In this article, we will learn how to create a top navigation and display on the pages using SPFx webpart.
 
This article uses PnP library to fetch the navigation items from a SharePoint list to display them dynamically. 
 

Overview

 
There might be a business requirement to show the top navigation items underneath a company brand, like the one displayed on the C# corner site.
 
Place SPFx Webpart Inside A DIV Tag On A Page
 
With the introduction of Modern pages, most organizations have restricted editing the default Master pages, set by the tenant administrators for various reasons, say, to maintain the company brand, ensure the same look and feel across all the other site collections etc.
 
After placing our webpart, the top navigation will be displayed as:
 
Place SPFx Webpart Inside A DIV Tag On A Page

Create WebPart

 
This article assumes that you know how to create a simple React SPFx web part. If you are first-timer, you can follow this article to create a React-based web part.
 
Let's jump in to the technical work. Start creating a webpart with the following options,
  • solution name - aadi-spfx-react-wp 
  • webpart name - TopNavigation 
  • Install PnP library dependencies
  • run the command in a terminal window: npm install react-bootstrap bootstrap --save 

Understanding the rendering of a webpart on a page

 
When a webpart is added to the page, it is rendered with in the webpart’s zone that was defined in the <WebPartName>WebPart.ts file, in the render() method.
 
But in our case, we want to render the webpart as Top Navigation, typically some html lines of code, and underneath the default SharePoint header.
 
So, the tricky part is to render the webpart by appending to an available OOTB div instead in a webpart’s zone.
 
The OOTB div IDs are different for classic and modern pages, please have a look by inspecting the element or by pressing F12.
 
When a webpart is created, the <webpartname>WebPart.ts file has the following code, we need to modify this,
  1. public render(): void {  
  2.     const element: React.ReactElement<ITopNavigationProps> = React.createElement(  
  3.       TopNavigation,  
  4.       {  
  5.         description: this.properties.description  
  6.       }  
  7.     );  
  8.   
  9.     ReactDom.render(element, this.domElement);  
  10.   }  
Modify the <webpartname>WebPart.ts file,
  1. public render(): void {  
  2.     const element: React.ReactElement<ITopNavigationProps> = React.createElement(  
  3.       TopNavigation,  
  4.       {  
  5.         description: this.properties.description,  
  6.         context: this.context  
  7.       }  
  8.     );  
  9.   
  10.     const tempDiv = document.createElement("div");  
  11.     ReactDom.render(element, tempDiv);  
  12.   
  13.     const divTopNavPlaceHolder: HTMLElement =  
  14.       (document.getElementById("suiteBarDelta") != null)  
  15.         ? document.getElementById("suiteBarDelta")  
  16.         : document.getElementById("SuiteNavPlaceHolder");  
  17.   
  18.     if (objectDefinedNotNull(divTopNavPlaceHolder)) {  
  19.       divTopNavPlaceHolder.appendChild(tempDiv);  
  20.     }  
  21.   }  
We will be first getting the OOTB div on a page, and then append the webpart html.
 
For classic page, the div suiteBarDelta is available and for Modern pages, SuiteNavPlaceHolder is available.
 

Get the top navigation items from a SharePoint list

 
I have optionally created a SharePoint list to fetch the items dynamically, the default view of SharePoint list is as,
 
Place SPFx Webpart Inside A DIV Tag On A Page
 
I will be uploading the list template with contents with this article, feel free to download and upload to your site.
 
Go to the component folder, <webpartname>.ts file, and add the componentDidMount() method to fetch the items from a SharePoint list:
  1. interface ISPListItemNav {  
  2.   Title: string;  
  3.   URL: string;  
  4.   Description: string;  
  5.   ParentMenu: string;  
  6. }  
  7.   
  8. interface ITopNavigationState {  
  9.   TopNavItems: ISPListItemNav[];  
  10. }  
  11.   
  12. export default class TopNavigation extends React.Component<ITopNavigationProps, ITopNavigationState, {}> {  
  13.   public constructor(props: ITopNavigationProps) {  
  14.     super(props);  
  15.     this.state = {  
  16.       TopNavItems: []  
  17.     };  
  18.   }  
  19.   
  20.   public render(): React.ReactElement<ITopNavigationProps> {  
  21.     return (  
  22.       <Navbar bg="dark" variant="dark" expand="lg">  
  23.         <Navbar.Brand href={this.props.context.pageContext.site.absoluteUrl}>{this.props.context.pageContext.web.title}</Navbar.Brand>  
  24.         <Navbar.Toggle aria-controls="basic-navbar-nav" />  
  25.         <Navbar.Collapse id="basic-navbar-nav">  
  26.           <Nav className="mr-auto">  
  27.             {this.state.TopNavItems.map(navItem => {  
  28.               return (  
  29.                 <Nav.Link href={navItem.URL}>{navItem.Title}</Nav.Link>  
  30.               );  
  31.             })}  
  32.             <NavDropdown title="Dropdown" id="basic-nav-dropdown">  
  33.               <NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>  
  34.               <NavDropdown.Item href="#action/3.2">Another action</NavDropdown.Item>  
  35.               <NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>  
  36.               <NavDropdown.Divider />  
  37.               <NavDropdown.Item href="#action/3.4">Separated link</NavDropdown.Item>  
  38.             </NavDropdown>  
  39.           </Nav>  
  40.         </Navbar.Collapse>  
  41.       </Navbar>  
  42.     );  
  43.   }  
  44.   
  45.   public async componentDidMount() {  
  46.     const navItems = await this.getNavigationItems();  
  47.     this.setState({  
  48.       TopNavItems: navItems  
  49.     });  
  50.   }  
  51.   
  52.   private getNavigationItems(): Promise<any> {  
  53.     return new Promise<any>((res, rej) => {  
  54.       sp.web.lists.getByTitle('Site Navigation').items  
  55.         .select('Title,URL,RoutingEnabled,RoutingRuleDescription,parentmenu,OrderNumber')  
  56.         .filter(`(parentmenu ne null or parentmenu ne '') and RoutingEnabled eq 1`)  
  57.         .orderBy('OrderNumber'true)  
  58.         .get()  
  59.         .then(spitems => {  
  60.           const navItems: ISPListItemNav[] = [];  
  61.           spitems.map(item => {  
  62.             navItems.push({  
  63.               Description: item.RoutingRuleDescription,  
  64.               Title: item.Title,  
  65.               URL: item.URL.Url,  
  66.               ParentMenu: item.parentmenu  
  67.             });  
  68.           });  
  69.           res(navItems);  
  70.         })  
  71.         .catch(e => rej(e));  
  72.     });  
  73.   }  
  74. }  
After placing the code changes, using gulp commands, bundle and Package the solution. Upload and deploy the package to your site collection.
 
Add the webpart to one of the page and see the contents are rendered to one of the DIV tag, as top navigation items.
 

Conclusion 

 
We learnt how to fetch the items from a SharePoint list using PnP library and append the webpart to an exisitng div tagon the page. 
 
Hope it helps someone, happy SharePointing!!