I have been working from a few different examples and nearly have a jslink file working with CSR overrides such that the 'title' value is used in the 'name' field and the title column is hidden.

The file is deployed to layouts and linked to the list views jslink property using myview.JSLink = "clienttemplates.js|kgjslinktitlefordocview.js".

I am having 2 issues that I am struggling with.

  1. My header template seems broken as it is displaying name in the header and all other column names are blank.

  2. How do I specify that the CSR overrides not take effect when Quickedit is clicked ?

    • I realize I can detect quickedit with renderCtx.isGridMode === true but I am not sure how to use this to leave the header and fields passing through as default.

Any pointers would be appreciated !

function $_global_kgjslinktitlefordocview(){
KG.jslink_linktitle = KG.jslink_linktitle || {};
KG.jslink_linktitle.Templates = KG.jslink_linktitle.Templates || {};
KG.jslink_linktitle.Functions = KG.jslink_linktitle.Functions || {};

KG.jslink_linktitle.Functions.RenderTemplateWithHiddenTitleColumn = function (renderCtx, fRenderHeaderColumnNames) {
    if (renderCtx.BaseViewID == "Callout") {
        return CalloutRenderHeaderTemplate(renderCtx);

    var listSchema = renderCtx.ListSchema;
    var listData = renderCtx.ListData;
    var ret = [];

    if (fRenderHeaderColumnNames == null) {
        fRenderHeaderColumnNames = true;
    ret.push('<thead id="');
    ret.push("js-listviewthead-" + renderCtx.wpq);
    ret.push('"><tr valign="top" class="ms-viewheadertr');
    if (fRightToLeft)
        ret.push(' ms-vhrtl');
        ret.push(' ms-vhltr');
    if (listSchema.TabularView != undefined && listSchema.TabularView == "1") {
        ret.push('<th class="ms-headerCellStyleIcon ms-vh-icon ms-vh-selectAllIcon" scope="col">');
        RenderSelectAllCbx(renderCtx, ret);
    if (fRenderHeaderColumnNames) {
        var fields = listSchema ? listSchema.Field : null;
        var counter = 1;

        for (var f in fields) {
            var field = fields[f];
            if (field.Name == "Title") {
            if (field.DisplayName == null)
            if (field.GroupField != null)
            field.counter = counter++;
            ret.push(spMgr.RenderHeader(renderCtx, field));
            if (IsCSRReadOnlyTabularView(renderCtx) && (field.CalloutMenu == "TRUE" || field.listItemMenu == "TRUE"))
    if (listSchema.TabularView == "1" && renderCtx.BasePermissions.ManageLists && renderCtx.ListTemplateType != 160) {
        ret.push('<th class="ms-vh-icon" scope="col" title=""><span class="ms-addcolumn-span"> </span></th>');
    return ret.join('');

KG.jslink_linktitle.Templates = { 'Header': KG.jslink_linktitle.Functions.RenderTemplateWithHiddenTitleColumn }

KG.jslink_linktitle.Templates.Fields = {
    'Title': {
        'View': function () { return null; }
    'LinkFilename': {
        'View': function (renderCtx) {

                if (renderCtx != null && renderCtx.CurrentFieldValue != null)
                    return renderCtx.CurrentFieldValue.toString();
                return '<a href="' + ctx.CurrentItem.FileRef + '">' + renderCtx.CurrentItem.Title + '</a>';               

KG.jslink_linktitle.BaseViewID = 1;

KG.jslink_linktitle.ListTemplateType = 101;


} $_global_kgjslinktitlefordocview();

Ok.. success ! In case someone else is struggling here is the script.


  • Works in MDS and non MDS mode if deployed as per script comments.
  • An old method of hiding the column using jQuery is included but commented out (ie. the OnPostRender has been commented). I thought it might be useful to someone.
  • I have not done extensive testing yet so mileage and performance may vary.

When not in QuickEdit -

  • Displays title in name column on document libraries.
  • Hides the title column. (Which must be in the view).
  • Document menu and previews work.
  • Sorting is still using the filename. (Could be a further regex in the header to change this ?)

In QuickEdit -

  • Shows the columns as normal allowing for updating of Title and Filename.

Drop me a note if you have any improvements to offer !

function $_global_kgjslinktitlefordocview() {

//jslink script to change a document library view so that Name of each document uses the Title value.
//Requires that Title is in the view, so the display template hides the Title field.

//Deployed straight to _layouts as file kgjslinktitlefordocview.js
//Used jslink property of listview as clienttemplates.js|kgjslinktitlefordocview.js
//Special note : do not use ~layouts token.  It is bugged and adds a script reference in the page that is missing a leading /.  
// - This breaks MDS as registerinit function cant match the path.
//The $_global_ wrapper function ensures that this works in MDS enabled sites.

KG.jslink_linktitle = KG.jslink_linktitle || {};
KG.jslink_linktitle.Templates = KG.jslink_linktitle.Templates || {};
KG.jslink_linktitle.Functions = KG.jslink_linktitle.Functions || {};

KG.jslink_linktitle.Functions.linkOnPostRender = function () {

    //This function has been left in as an alternate example.
    //I did not like that the column appears for a moment before it gets hidden. 

    //Function to hide the Title column on PostRender using jQuery based on the code by Muawiyah Shannak.

    //The callback routine is because in my environment I am registering jQuery as on-demand.
    var callback_linkonpostrender = function () {
        //Hide "Title" column if not in QuickEdit mode
        if (!this.ctx.inGridMode) {

            var cell = jQuery("div [displayname='Title']").closest('th');
            var cellindex = typeof cell.cellIndex;

            if (cellindex != null && cellindex != 'undefined') {
                var cellIndex = cell[0].cellIndex + 1;

                jQuery('td:nth-child(' + cellIndex + ')').hide();
                jQuery('th:nth-child(' + cellIndex + ')').hide();

    var namespace = "$_global_kgjquery";
    var key = "kgjquery.js";
    var bSync = true;

    if (typeof $_global_kgjquery == 'undefined') {

        SP.SOD.executeFunc(key, namespace, function () {
            console.log(key + ": Type available (" + namespace + ")");
        }, bSync);

        SP.SOD.executeOrDelayUntilScriptLoaded(callback_linkonpostrender(), key);
    else {
        console.log(namespace + " found so jQuery should be loaded.");

KG.jslink_linktitle.Functions.getDefaultFieldHtml = function (renderCtx, field, listItem, listSchema) {

    //Copy Paste of Jim Browns awesome helper function to return default CSR field rendering

    var renderingTemplateToUse = null;

    var fieldRenderMap = {
        Computed: new ComputedFieldRenderer(field.Name),
        Attachments: new AttachmentFieldRenderer(field.Name),
        User: new UserFieldRenderer(field.Name),
        UserMulti: new UserFieldRenderer(field.Name),
        URL: new UrlFieldRenderer(field.Name),
        Note: new NoteFieldRenderer(field.Name),
        Recurrence: new RecurrenceFieldRenderer(field.Name),
        CrossProjectLink: new ProjectLinkFieldRenderer(field.Name),
        AllDayEvent: new AllDayEventFieldRenderer(field.Name),
        Number: new NumberFieldRenderer(field.Name),
        BusinessData: new BusinessDataFieldRenderer(field.Name),
        Currency: new NumberFieldRenderer(field.Name),
        DateTime: new DateTimeFieldRenderer(field.Name),
        Text: new TextFieldRenderer(field.Name),
        Lookup: new LookupFieldRenderer(field.Name),
        LookupMulti: new LookupFieldRenderer(field.Name),
        WorkflowStatus: new RawFieldRenderer(field.Name)

    if (field.XSLRender == '1') {

        renderingTemplateToUse = new RawFieldRenderer(field.Name);
    else {

        renderingTemplateToUse = fieldRenderMap[field.FieldType];
        if (renderingTemplateToUse == null)
            renderingTemplateToUse = fieldRenderMap[field.Type];

    if (renderingTemplateToUse == null)

        renderingTemplateToUse = new FieldRenderer(field.Name);

    return renderingTemplateToUse.RenderField(renderCtx, field, listItem, listSchema);

KG.jslink_linktitle.Functions.renderHeaderTemplateForLibraries = function (renderCtx, fRenderHeaderColumnNames) {

    //Set the amount of views to display on the change view control (viewData.length -3 for all)
    var viewData = eval(renderCtx.ListSchema.ViewSelectorPivotMenuOptions);
    ClientPivotControl.prototype.SurfacedPivotCount = viewData.length - 3;

    //Get HTML from standard RenderHeaderTemplate function
    var htmlheader = RenderHeaderTemplate(renderCtx, fRenderHeaderColumnNames);

    //If not in QuickEdit then try to RegEx replace out all of the header for the chosen column
    // (Note : Uses a negative lookahead to make sure its the closest <th tag.) 
    if (!renderCtx.inGridMode) {           
        var re = new RegExp('(<th (?:(?!<th ).)*DisplayName=\\"Title\\".*?<\/th>)', 'g');
        var match = re.exec(htmlheader);
        var replaced = htmlheader.replace(re, "");
        return replaced;

    //If in QuickEdit then just display default header
    return htmlheader;

KG.jslink_linktitle.Functions.renderBodyTemplateForLibraries = function (renderCtx) {       

    //If not in QuickEdit then try to modify the renderCtx.ListSchema.Field array to remove the field that we dont want rendered in the body HTML. 
    if (!renderCtx.inGridMode) {

        var temptitlefield = {};
        var temptitlefieldnumber = -1;
        //In the standard renderBodyTemplate function, the ListSchema.Field array seems to get used to build the <td> values for each field.
        //Sneaking in and removing the field that we dont want displayed in the body and then calling the default RenderBodyTemplate seems to work.
        for (var i = 0; i < renderCtx.ListSchema.Field.length; i++) {
            if (renderCtx.ListSchema.Field[i].FieldTitle === "Title") {
                temptitlefield = renderCtx.ListSchema.Field[i];
                temptitlefieldnumber = i;
                renderCtx.ListSchema.Field.splice(i, 1);
        //Get HTML from standard RenderBodyTemplate function 
        //(using our now modified renderCtx with the field removed from ListSchema if not in QuickEdit)
        var htmlbody = RenderBodyTemplate(renderCtx);
        //Here we reverse our change if we made one.  Without this the title field was readonly in QuickEdit until refreshing the page.
        //I assume the renderCtx object is reused so we add the Title field back in.
        if (temptitlefieldnumber != -1)
        return htmlbody;

    //Assuming we are in QuickEdit we just use default RenderSPGridBody
    var gridbody = RenderSPGridBody(renderCtx);
    return gridbody;

KG.jslink_linktitle.Templates = {
    'Header': KG.jslink_linktitle.Functions.renderHeaderTemplateForLibraries,
    'Body': KG.jslink_linktitle.Functions.renderBodyTemplateForLibraries

KG.jslink_linktitle.Templates.Fields = {
    //'Title': {
      //  'View': function () { return null; }
    'LinkFilename': {
        'View': function (ctx) {

            //Use Jim Browns helper function to get whatever the base HTML for the field should be.
            var html = KG.jslink_linktitle.Functions.getDefaultFieldHtml(ctx, ctx.CurrentFieldSchema, ctx.CurrentItem, ctx.ListSchema);
            //Locate the LinkFilename display text in the html.
            var filename = ">" + ctx.CurrentItem.FileLeafRef.substring(0, (ctx.CurrentItem.FileLeafRef.lastIndexOf("."))) + "<";

            //If not in QuickEdit and if Title exists and is not blank then use the Title as the LinkFilename display text.
            if (!ctx.inGridMode && ctx.CurrentItem.Title != '' && ctx.CurrentItem.Title != '' && typeof ctx.CurrentItem.Title != "undefined") {
                var newhtml = html.replace(filename, ">" + ctx.CurrentItem.Title + "<");
                return newhtml;

            //Otherwise return default HTML.
            return html; 

//This was my first attempt by using jQuery to hide the column during PostRender.
//KG.jslink_linktitle.OnPostRender = KG.jslink_linktitle.Functions.linkOnPostRender;

KG.jslink_linktitle.BaseViewID = 1;

KG.jslink_linktitle.ListTemplateType = 101;

//Make the magic happen....

} $_global_kgjslinktitlefordocview();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.