I am trying to implement Bootstrap 3 Tabs in an App for Office but I keep getting the following exception:
Unhandled exception at line 1453, column 2 in https://localhost:44303/Scripts/jquery-2.1.3.js
0x800a139e - JavaScript runtime error: Syntax error, unrecognized expression: #profile&_xdm_Info=null|null|null
Based on that it seems like Outlook 365 is appending &_xdm_Info=null|null|null
but I don't understand why, how, or what to do about it if true.
Here is a whole simplified Home.html
to illustrate the issue. Note it will work fine is a JSFiddle or Bootply.
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<title></title>
<script src="../../Scripts/jquery-2.1.3.js" type="text/javascript"></script>
<link href="../../Content/bootstrap.css" rel="stylesheet" type="text/css"/>
<script src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js" type="text/javascript"></script>
<script src="../../Scripts/bootstrap.js" type="text/javascript"></script>
<link href="../App.css" rel="stylesheet" type="text/css"/>
<script src="../App.js" type="text/javascript"></script>
<link href="Home.css" rel="stylesheet" type="text/css"/>
<script src="Home.js" type="text/javascript"></script>
</head>
<body>
<div id="content-main">
<!--<div class="padding">-->
<ul class="nav nav-pills">
<li class="active">
<a href="#home" data-toggle="tab">Home</a>
</li>
<li>
<a href="#profile" data-toggle="tab">Profile</a>
</li>
<li>
<a href="#messages" data-toggle="tab">Messages</a>
</li>
</ul>
<div id='content' class="tab-content">
<div class="tab-pane active" id="home">
<ul>
<li>home</li>
<li>home</li>
<li>home</li>
<li>home</li>
<li>home</li>
<li>home</li>
<li>home</li>
</ul>
</div>
<div class="tab-pane" id="profile">
<ul>
<li>profile</li>
<li>profile</li>
<li>profile</li>
<li>profile</li>
<li>profile</li>
<li>profile</li>
<li>profile</li>
</ul>
</div>
<div class="tab-pane" id="messages">
</div>
</div>
</div>
</body>
</html>
Here is my call stack -->
Sizzle.error [jquery-2.1.3.js] Line 1453 Script
Sizzle.tokenize [jquery-2.1.3.js] Line 2067 Script
Sizzle.select [jquery-2.1.3.js] Line 2474 Script
Sizzle [jquery-2.1.3.js] Line 850 Script
find [jquery-2.1.3.js] Line 2690 Script
jQuery.fn.init [jquery-2.1.3.js] Line 2798 Script
jQuery [jquery-2.1.3.js] Line 76 Script
Tab.prototype.show [bootstrap.js] Line 2050 Script
Anonymous function [bootstrap.js] Line 2123 Script
each [jquery-2.1.3.js] Line 374 Script
jQuery.prototype.each [jquery-2.1.3.js] Line 139 Script
Plugin [bootstrap.js] Line 2118 Script
clickHandler [bootstrap.js] Line 2147 Script
jQuery.event.dispatch [jquery-2.1.3.js] Line 4429 Script
elemData.handle [jquery-2.1.3.js] Line 4115 Script
TL;DR: You can work around this issue by using <a data-target="#profile">
instead of <a href="#profile">
Explanation: The Apps for Office framework relies on the office.js that is loaded from the app to communicate cross-frame with the host application (like Outlook). In order to preserve some states across page navigation, we append some information as URL fragment (like _xdm_Info) so that the second page of the app can talk to the host application as well.
Meanwhile, Bootstrap uses <a>
tags to navigate between tags, which is obviously the tag used for navigation used in HTML. From on here, I'll be honest and say I'm guessing a little bit, but it seems like for whatever reason, we are modifying the href attribute of the <a>
tags from the app to insert the state information as URL fragment.
From Bootstrap's tab.js:
Tab.prototype.show = function () {
var $this = this.element
var $ul = $this.closest('ul:not(.dropdown-menu)')
var selector = $this.data('target')
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
As you can see here, the selector that the logic uses to display the tab is first trying to get the data-target
attribute from the clicked tab selector, and then falls back to trying to use the href
attribute. From the error you attached, when it falls back to the href
attribute, the value of that attribute is #profile&_xdm_Info=null|null|null
instead of just #profile
. This is why when var $target = $(selector)
is run, jQuery's selector engine, Sizzle, complains because that is not a valid CSS selector.
This is why the problem is eliminated by using the data-target
attribute instead. I believe that since it is not an attribute traditionally used for navigation, we don't muck with it.
Follow-up: I'm going to confirm that this is by design and not a bug, and that there is no better way to append _xdm_Info
to the URL fragment. IIRC, Bootstrap is not the only Javascript/jQuery plugin that uses the href
attribute to communicate CSS selectors to code, and we should try to enable this scenario instead of encouraging developers to work around it. If this is indeed the only way to accomplish this, I'll push to have this included in the documentation.
EDIT: I've followed up with the team that builds the Apps for Office framework about this issue. It seems that indeed altering the href attributes of anchor tags in the app was part of the design. However, the discussion on whether this was the right way to do it is moot because they have moved away from this design - new versions of office.js will no longer do this, so hopefully this will not block any other scenarios going forward.