Question

I have a custom field type for SharePoint 2010/2007, but after migrated to SharePoint 2013, I met a problem.
Here is what I defined for "RenderPattern" in fldtypes_myfield.xml

<RenderPattern Name="DisplayPattern">
<HTML><![CDATA[<script type="text/javascript" src="/_layouts/myfield/myjs.js"></script>]]></HTML>
<HTML><![CDATA[<img src="/_layouts/images/myfield/pic.png" onload="myfun()">]]></HTML>
</RenderPattern>

myfun() is defined in myjs.js, so when pic.png is loaded, it will execute the function. It works fine in 2010/2007 and 2010 user experience in SharePoint 2013.
In 2013, under 2013 user experience, when I view the list that contains myfield, it throws the error:

myfun is not defined  

So, the reason is obvious, myjs.js is not added to the page. (The js link is correct, copy link to address bar and browser can display it)
So, my question is: why is myjs.js not registered?
Any ideas? Thanks in advance.

Update:
It doesn't work in "ListView" page, but it works in "DisplayForm" page. "CAMLRendering" is set to true. Under "ListView" page, if you check the field with web developer tool, the html markup inside the is:

<SCRIPT type=text/javascript src="/_layouts/myfield/myjs.js"></SCRIPT>  
<DIV><IMG src="/_layouts/images/myfield/pic.png" onload="myfun()"></DIV>  

Does SharePoint 2013 forbid the js to load?

Was it helpful?

Solution

After debugging, I found there is a variable called "WPQ2ListData" that stores the list data content, its format is:

var WPQ2ListData = {
"Row": [{
    "ID": "1",
    "PermMask": "0x7fffffffffffffff",
    "FSObjType": "0",
    "Title": "A",
    "FileLeafRef": "1_.000",
    "Created_x0020_Date.ifnew": "1",
    "FileRef": "\u002ftest\u002fLists\u002fRatingList\u002f1_.000",
    "File_x0020_Type": "",
    "File_x0020_Type.mapapp": "",
    "HTML_x0020_File_x0020_Type.File_x0020_Type.mapcon": "",
    "HTML_x0020_File_x0020_Type.File_x0020_Type.mapico": "icgen.gif",
    "ContentTypeId": "0x01008AD43674D321D3428ACAFC1C3BD2971E",
    "MyField": "<script type=\"text/javascript\" src=\"/_layouts/myfield/myjs.js\"></script><img src=\"/_layouts/images/myfield/pic.png\" onload=\"myfun()\">"
}],
"FirstRow": 1,
"LastRow": 1,
"FilterLink": "?",
"ForceNoHierarchy": "1",
"HierarchyHasIndention": ""

}

Pay attention to "MyField", it has the content which should be rendered.
In the page, "WPQ2ListData" is used in one place:

ctx = new ContextInfo();  
//other assignments
ctx.ListData = WPQ2ListData;

Then "ctx" will be passed to the function " RenderListView", which is responsible for rendering the list view. This function is defined in "clienttemplates.js". If you dive into it, you will find that it will call another function called "SPClientRenderer.Render", in this function, it will generate the whole html content for the list data, and insert it to a td (maybe a div) as innerHTML.

var result = SPClientRenderer.RenderCore(renderCtx);  
if (result != null && result != '') {
    if (node.tagName == "DIV" || node.tagName == "TD") {  
        if (renderCtx.fHidden)  
            if (renderCtx.fHidden)  
        node.innerHTML = result;
    }else{
        //create div element, and insert result as innerHTML   
    }
}  

Pay attention to "node.innerHTML = result", because if some string contents is inserted to one element as innerHTML and the content contains JavaScript code, the js code will not be executed. And this is why "myjs.js" is not added to page! I think this is one of the big differences on rendering list view in 2010 and 2013, in 2010, list data html is output to page directly, while it is inserted to page dynamically in 2013.

Solution:
Actually, there is still one place that contains js code and can execute the code. It's "onload" in img. So, why don't we register our js file here? And after myjs.js is loaded, let it call myfun()!

var head = document.getElementsByTagName("head")[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/_layouts/myfield/myjs.js';
script.onload = script.onreadystatechange = function() {
    myfun();
};
head.appendChild(script);  

And for better performance, we don't have to register myjs.js for each row, we should check if it exists first.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top