I have had a strange bug. The comment text box in a OOB SharePoint 2013 blog doesn’t appear. It only says: “There are no comments for this post.” In this blog post I’ll tell you how I found the bug and I’ll show you how you can temporarily bring the commenting to life.
I have had luck. While it doesn’t work on the Test Environment, it does actually work on my development machine. The comments text box is rendered as an OnPostRender action in the blog comments display template. After debugging the javascript in hours and comparing the two environments, I just could confirm that displaytemplates are the same. There are two main javascript files that are involved:
- clienttemplates.js
- sp.ui.blogs.js
So it must some differences elsewhere. The build versions of the environments are
- Working Environment: 15.0.4420.1017 (SharePoint 2013 RTM)
- Not-working Environment: 15.0.4517.1005 (SharePoint 2013 June CU)
No errors are thrown, even on the blog where it doesn’t work. It is because of the permissive try-catch in this function that wraps rendering of the comment text box:
function CallFunctionWithErrorHandling(fn, c, erv, execCtx) { if (SPClientRenderer.IsDebugMode(c)) { return fn(); } try { return fn(); } catch (e) { if (c.Errors == null) c.Errors = []; try { e.ExecutionContext = execCtx; if (Boolean(SPClientRenderer.AddCallStackInfoToErrors) && typeof execCtx == "object" && null != execCtx) { execCtx.CallStack = ULSGetCallstack(CallFunctionWithErrorHandling.caller); } } catch (ignoreErr) { } c.Errors.push(e); return erv; } }
This is where the error occurs but doesn’t appear in the script console. Here is the error:
ExecutionContext: Object message: "Syntax error, unrecognized expression: #{B808453A-141A-4091-8467-B6915B8B3E99}-{6C3BF638-43F2-4F12-A297-DEF18635540E}-CommentContainer" stack: "Error: Syntax error, unrecognized expression: #{B808453A-141A-4091-8467-B6915B8B3E99}-{6C3BF638-43F2-4F12-A297-DEF18635540E}-CommentContainer at Error () at Function.st.error (jquery-1.9.1.min.js:4:10926) at ft (jquery-1.9.1.min.js:4:17138) at wt (jquery-1.9.1.min.js:4:19823) at Function.st (jquery-1.9.1.min.js:4:6140) at b.fn.extend.find (jquery-1.9.1.min.js:4:20941) at b.fn.b.init (jquery-1.9.1.min.js:3:1126) at b (jquery-1.9.1.min.js:3:206) at /ScriptResource.axd:344:53 at _foreach (ScriptResource.axd:33:17)"
What? jQuery error? Why?
It turns out that jQuery is used to get the html element for the comments area in the server with June CU, but not in the Server with SharePoint 2013 RTM.
To get the reference to the comments area, it invokes the $get method, which invokes Sys.get which in some cases invokes the jQuery (if present)
$get implementation in SP 2013 June CU
These are the functions which can be found in the SharePoint 2013 June CU (15.0.4517.1005)
$get = $type.getElementById = function DomElement$getElementById(id, element) { /// <summary locid="M:J#Sys.UI.DomElement.getElementById">Finds an element by id.</summary> /// <param name="id" type="String">The id of the element to find.</param> /// <param name="element" domElement="true" optional="true" mayBeNull="true"></param> /// <returns domElement="true" mayBeNull="true">The element, or null if it was not found.</returns> var e = Function._validateParams(arguments, [ {name: "id", type: String}, {name: "element", mayBeNull: true, domElement: true, optional: true} ]); if (e) throw e; return Sys.get("#" + id, element || null); }
get: function get(selector, context) { /// <summary>Queries the DOM for a single DOM element.</summary> /// <validationOptions enabled="false" /> /// <param name="selector"> /// Selector for a DOM element based on id (#<id>), class (.<name>), or tag name (<tagname>). //// More complex selectors may be used if jQuery is loaded. /// If multiple elements match the selector, the first one is returned. /// </param> /// <param name="context" optional="true" mayBeNull="true">An element, array of elements, or Sys.UI.TemplateContext to restrict the query within.</param> /// <returns>The matching element, or null if none match.</returns> return (context && typeof(context.get) === "function") ? context.get(selector) : this._find(selector, context, true); },
_find: function _find(selector, context, single, filter) { ... else if (window.jQuery) { if (!filter) { found.push.apply(found, jQuery(selector, context).get()); } if (includeSelf) { found.push.apply(found, jQuery(context).filter(selector).get()); } } ... },
older (but working) $get implementation in SharePoint 2013 RTM
These are two versions which can be found in the older SharePoint Release: RTM (15.0.4420.1017)
var $get = Sys.UI.DomElement.getElementById = function Sys$UI$DomElement$getElementById(id, element) { /// <summary locid="M:J#Sys.UI.DomElement.getElementById" /> /// <param name="id" type="String"></param> /// <param name="element" domElement="true" optional="true" mayBeNull="true"></param> /// <returns domElement="true" mayBeNull="true"></returns> var e = Function._validateParams(arguments, [ {name: "id", type: String}, {name: "element", mayBeNull: true, domElement: true, optional: true} ]); if (e) throw e; if (!element) return document.getElementById(id); if (element.getElementById) return element.getElementById(id); var nodeQueue = []; var childNodes = element.childNodes; for (var i = 0; i < childNodes.length; i++) { var node = childNodes[i]; if (node.nodeType == 1) { nodeQueue[nodeQueue.length] = node; } } while (nodeQueue.length) { node = nodeQueue.shift(); if (node.id == id) { return node; } childNodes = node.childNodes; for (i = 0; i < childNodes.length; i++) { node = childNodes[i]; if (node.nodeType == 1) { nodeQueue[nodeQueue.length] = node; } } } return null; }
It doesn’t even go to Sys.get and doesn’t even try to use jQuery.
The id of the comment are is a combination of two guids and -CommentContainer. jQuery fails to select an element with this id:
This error happens when you try to select this html element with jQuery in the web console:
Temporary solution
The solution is to prevent using jQuery for this element selection. Hopefully new updates of SharePoint will address this issue. Until then we have to tweak the $get method. To do it very carefully, we can copy the old $get function and call it $get_asFoundInRTM and copy the new $get and call it $get_asFoundInJuneCU
The $get function has to rewritten like that:
$get_asFoundInJuneCU = $get $get = function () { var id = arguments[0]; return /\{/.test(id) ? $get_asFoundInRTM.apply(null, arguments) : $get_asFoundInJuneCU.apply(null, arguments); }
This javascript code has to be loaded after two ScriptResource.axd files. Please be very careful if you have to implement this temporary fix. Probably it is better to wait for an official Microsoft bug fix.
UPDATE 2014-03-27
I have discovered that in SP1 it is already solved.
