Having additional navigation for your content is a must-have in any modern website.
One example of that kind of navigation is Anchor/Jump links navigation. Simple, but very effective. We are simply using anchor links to jump to a specific part of the page. But since we are using the Optimizely CMS we are gonna make it more dynamic and automate the creation of anchor tags and links.
The Solution
The idea is to create navigation with all anchor links that are on the page. The simplest way is to have a page template with Content Area with blocks and then “just” render the anchor link for every block. To achieve that we are gonna make an interface IJumpLinkable so that every block that we want to have an anchor tag for the anchor navigation will inherit. In that interface we will have a string property that will represent the anchor navigation item label.
public interface IJumpLinkable : IContentData { string LinkText { get; set; } }
Now add this interface in every block that we want to use. We need to implement the property from the interface and to make it a fail-safe, we will tap in in the publishing event ( Opti Documentation ) and check if the link label is empty, then use the block’s name as a fallback value.
public class MyBlock : BaseBlock, IJumpLinkable { .... [Display( GroupName = GroupNames.Navigation, Name = "Anchor/Jump Link Label", Description = "", Order = 10)] public virtual string LinkText { get; set; } .... public void PublishingContent(object sender, ContentEventArgs e) { if (string.IsNullOrEmpty(LinkText)) { LinkText = (this as IContent).Name; } } }
In our PageViewModel we have to add a dictionary object that will have key -> link label and value -> block ID pairs.
public Dictionary<string, string> JumpLinks { get; set; }
Page Controller
In our page controller after we create a page view model, we need to populate that dictionary object with the content area items. We will scan the items list for the content that is of IJumpLinkable type and from that content (block) we will take the link label and content ID.
... public ActionResult Index(MyContentPage currentContent) { var viewModel = CreateModel(currentContent); if(currentContent.MainContentArea != null) { var jumpLinkItems = currentContent.MainContentArea.FilteredItems.GetContentItems<IJumpLinkable>(); var links = new Dictionary<string, string>(); foreach (var jumpLinkItem in jumpLinkItems.Where(jumpLinkItem => !string.IsNullOrWhiteSpace(jumpLinkItem.LinkText))) { links.Add(jumpLinkItem.LinkText, ((IContent)jumpLinkItem).ContentLink.ID.ToString()); } viewModel.JumpLinks = links; } return View("MyContentPage", viewModel); } ...
Now that we have a list of links, all we have to do now is to render that list in our page view. We need to loop twice through that list, first to render the navigation menu and second to render the anchor tags before rendering each block from the content area. We have to render the blocks from the content area in the loop because we need to put an anchor tag with the unique ID before every block. In this example the anchor ID is constructed with the prefix “content” plus the block’s ID. ( W3School Anchor example )
Rendering the Navigation Menu:
if (Model.JumpLinks != null && Model.JumpLinks.Count > 0) { <section> <div class="container"> @foreach (var link in Model.JumpLinks) { <div> <a href="#content-@link.Value"> @link.Key </a> </div> } </div> </section> }
Rendering the content area:
<section class="content"> <div @Html.EditAttributes(x => x.CurrentContent.MainContentArea)> @if (Model.CurrentContent.MainContentArea != null) { foreach (var item in Model.CurrentContent.MainContentArea.Items) { <a id="content-@item.ContentLink.ID"></a> @Html.DisplayFor(x => item.ContentLink) } } </div> </section>
And that’s it!