Back to Cleverworkarounds mainpage
 

More SharePoint Branding - Customisation using JavaScript - Part 1

Hi all

I thought with my last post that involved XSL/XSLT, I’d escape from horrid programming languages and write about more interesting topics but it wasn’t meant to be. This time round I had to delve back into the world of JavaScript - something I swore that I would never do again after a painful encounter back in 2000. (Yep, it’s taken me 8 years to face it again!)

But like everything else with SharePoint, by being a ‘specialist‘, you seem to have to use more technologies and IT disciplines than you would think possible.

As I progressed writing this article, I realised that I was delving back into branding again and toyed with the idea of making this part 8 of the branding series. But the governance topic in part 7 for me rounded off that series of posts nicely, so I will deal with this separately for now and perhaps refresh that series in the future.

Like a vast majority of my posts, this will also be a mini series.

CleverWorkArounds Coffee requirement rating (for Metrosexual web developers): image

CleverWorkArounds Coffee requirement rating (for the rest of us ): image image image

[Quick Navigation: Part 2, Part 3, Part 4, Part 5 and Part 6]

The problem in a nutshell

We have a list, but we want to hide some of the fields from being displayed in the edit or new item screens. By default when you create a list and add columns, you can hide columns via custom views, but when you create or edit an item you get the lot, whether you like it or not.

*Sigh…If only columns could have permissions as well as items, I wouldn’t have to document this workaround.*

The Workaround

As it happens, Microsoft’s SharePoint designer team indirectly provided a workaround for this in their post on using a javascript technique to manipulate form field such as set default values. Then Edin Kapić  described a modified version where he used the javascript technique for hiding columns.

Both methods make use of some javascript function called “getTagFromIdentifierAndTitle”. The function is below.

function getTagFromIdentifierAndTitle(tagName, identifier, title)
{
   var len = identifier.length;
   var tags = document.getElementsByTagName(tagName);
   for (var i=0; i < tags.length; i++)
   {
      var tempString = tags[i].id;
      if (tags[i].title == title && (identifier == “” || tempString.indexOf(identifier) == tempString.length - len))
        {
         return tags[i];
         }
    }
   return null;
}

This function works, because *most* SharePoint form controls include the *type* of element in their id. Here are some examples of SharePoint form element names in HTML. The first two are text boxes and the last is a drop down list. (Warning: The HTML code behind is pretty feral, but the end of the control names are consistent :)

image

<input name=”ctl00$m$g_d7c61fb0_74de_42f1_938f_a2aa0d9d3212$ctl00$ctl04$ctl00$ctl00$ctl00$ctl04$ctl00$ctl00$TextField” type=”text” maxlength=”255″ id=”ctl00_m_g_d7c61fb0_74de_42f1_938f_a2aa0d9d3212_ctl00_ctl04_ctl00_ctl00_ctl00_ctl04_ctl00_ctl00_TextField” title=”Device Name” class=”ms-long” />

<input name=”ctl00$m$g_d7c61fb0_74de_42f1_938f_a2aa0d9d3212$ctl00$ctl04$ctl01$ctl00$ctl00$ctl04$ctl00$ctl00$TextField” type=”text” value=”5777334″ maxlength=”255″ id=”ctl00_m_g_d7c61fb0_74de_42f1_938f_a2aa0d9d3212_ctl00_ctl04_ctl01_ctl00_ctl00_ctl04_ctl00_ctl00_TextField” title=”Serial Number” class=”ms-long” />

<select name=”ctl00$m$g_d7c61fb0_74de_42f1_938f_a2aa0d9d3212$ctl00$ctl04$ctl03$ctl00$ctl00$ctl04$ctl00$DropDownChoice” id=”ctl00_m_g_d7c61fb0_74de_42f1_938f_a2aa0d9d3212_ctl00_ctl04_ctl03_ctl00_ctl00_ctl04_ctl00_DropDownChoice” title=”Device Type” class=”ms-RadioText”>
                    <option selected=”selected” value=”Router”>Router</option>
                    <option value=”Switch”>Switch</option>
                    <option value=”Wireless Access Point”>Wireless Access Point</option>

</select>

Inside the function there is a call to the javascript built-in method getElementsByTagName. This will return all matching HTML elements of a given type. So, for example, if you wanted to return all table cells (such as <td>this is a cell</td>) then you would make a call like:

tablecells = document.getElementsByTagName(“td”); 

So let’s walk through the getTagFromIdentifierAndTitle function, using the example of passing in the parameters of “select”, “textfield” and “Serial Number”.

var control = getTagFromIdentifierAndTitle(“Input”,“TextField”,“Serial Number”);

This function gets all of the “Input” tags from the rendered document via getElementsByTagName(). It then loops through each matched tag and matches the ones that have “TextField” in the id and have a title of “Serial Number”. It then returns the referenced control, so you can manipulate it.

Once you have a reference to the control, you can hide it, or its parent control/container via styles. Below is a hideFields() function that calls getTagFromIdentifierAndTitle and then uses styles to hide the table row that the control resides in.

<script language=“javascript” type=“text/javascript”>

_spBodyOnLoadFunctionNames.push(“hideFields”);

function hideFields() {
   var control = getTagFromIdentifierAndTitle(“Input”,“TextField”,“Serial Number”);
   control.parentNode.parentNode.parentNode.style.display=“none”;
}
</script>

So, what is this parentNode.parentNode.parentNode stuff? Well, we want to hide the entire row, not just the control. Each time you reference a parent node, you are referring to the HTML element that this control is inside. Below is a simplified HTML illustration of the “Device Name” field where I removed the horrible control ID and name for the sake of readability. You can see that it is a table row with a cell for the title and a cell for the “device name” textbox.

<TR>
   <TD nowrap=”true” valign=”top” width=”190px” class=”ms-formlabel”>
      <H3 class=”ms-standardheader”><nobr>Device Name<span class=”ms-formvalidation”>*</span></nobr>
      </H3>
   </TD>
   <TD valign=”top” class=”ms-formbody” width=”400px”>
      <span dir=”none”>
——–><input name=”" type=”text” maxlength=”255″ id=”" title=”Device Name” class=”ms-long” /><br>
      </span>
  </TD>
</TR>

Working outwards from our <input> tag noted above we can see that it is inside a <SPAN> tag, then a <TD> tag and finally the <TR>.

  • control.parentNode refers to <SPAN dir=”none”>
  • control.parentNode.parentNode refers to <TD valign=”top” class=”ms-formbody” width=”400px”>
  • control.parentNode.parentNode.parentNode refers to <TR> and it is this element that we hide.

This works a treat and can be applied to both NewForm.aspx and EditForm.aspx. In the example below I passed the title of “Device Name” to the function as follows:

function hideFields() {  

   var control = getTagFromIdentifierAndTitle(“Input”,“TextField”,“Device Name”);
   control.parentNode.parentNode.parentNode.style.display=“none”;

}

Before:

image

After: (No device name!)

image

The catch(es)!

If you have taken away anything from reading my articles, it is never a workaround without a caveat or two! This technique relies on four assumptions:

  • HTML form controls consistently include their type in their ugly, long ID and name attributes
  • HTML form controls consistently supply a “title” property
  • The node that we want to hide is always 3 parent nodes from the control.
  • Microsoft’s table of SharePoint column types and their tag names and identifiers is correct :-)

On all counts, these assumptions are not consistent and you will have to modify your code.

Catch 1 - Incorrect information

First, let’s look at the tag names and identifiers for controls as per Microsoft’s own blog post.

SharePoint Field Type identifier tagName
Single Line of Text TextField input
Multiple Lines of Text TextField input
Number TextField input
Currency TextField input
Choice (dropdown) DropDownChoice select
Lookup (single) Lookup select
Lookup (multiple) SelectCandidate; SelectResult select
Yes/No BooleanField input

 

My examples thus far have been based around a single line of text. Let’s try hiding a multiple line of text column then shall we? As it turns out the list I am using do demonstrate concepts in this blog post conveniently has a column called “comments” that is set to the type “Multiple Lines of Text”. According to everything we have done so far, the code should simply be:

function hideFields() {

   var control = getTagFromIdentifierAndTitle(“Input”,“TextField”,“Comments”);
   control.parentNode.parentNode.parentNode.style.display=“none”;

}

…and the result? Not a damn thing! The Comments field is still determined to be visible.

image

Damn, what went wrong?? It worked for Device Name!

If we look in the source code for the reference to this column, we see the following:

<textarea name=”ctl00$m$g_242eacf0_89de_4c80_a74d_ec7ef12f2083$ctl00$ctl04$ctl06$ctl00$ctl00$ctl04$ctl00$ctl00$TextField” rows=”6″ cols=”20″ id=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl06_ctl00_ctl00_ctl04_ctl00_ctl00_TextField” title=”Comments” class=”ms-long” dir=”none”></textarea>

Well, well, well!

This is a “textarea”, not an “input” at all. Therefore the tagName is wrong. Microsoft’s table should in fact read…

SharePoint Field Type identifier tagName
Multiple Lines of Text TextField textarea

So the revised code is:

function hideFields() {

   var control = getTagFromIdentifierAndTitle(“Textarea”,“TextField”,“Comments”);
   control.parentNode.parentNode.parentNode.style.display=“none”;

}

And when we refresh? (drumroll)

image

Well. it sorta worked :-) We have hidden the control but not the label.

… and with that we come to catch 2.

Catch 2 - Varying parentNodes

We’ve already learned in the previous section that we want to hide the entire row, so we need to work out how many parentNodes away the surrounding <TR> tag is. Below is the simplified HTML code containing the ‘Comments’ control above.

<TR>
  <TD nowrap=”true” valign=”top” width=”190px” class=”ms-formlabel”>
    <H3 class=”ms-standardheader”>
      <nobr>Comments</nobr>
    </H3>
  </TD>
  <TD valign=”top” class=”ms-formbody” width=”400px”>
    <span dir=”none”>
      <span dir=”ltr”>
        <textarea name=”" rows=”6″ cols=”20″ id=”" title=”Comments” class=”ms-long” dir=”none”>
        </textarea>
      </span>
    </span>
  </TD>
</TR>

Working outwards from out <textarea> tag noted above we can see that is inside a <SPAN> tag, another <SPAN> tag, then a <TD> tag and finally the <TR>.

  • control.parentNode refers to <SPAN dir=”ltr”>
  • control.parentNode.parentNode refers to <SPAN dir=”none”>
  • control.parentNode.parentNode.parentNode refers to <TD valign=”top” class=”ms-formbody” width=”400px”>
  • control.parentNode.parentNode.parentNode.parentNode refers to <TR> and it is this element that we hide.

So we now know our second issue. The hidefields() function needs to add an extra parent node when hiding a column that is a “multiple lines of text” type.

function hideFields() {

   var control = getTagFromIdentifierAndTitle(“Input”,“TextField”,“Comments”);
   control.parentNode.parentNode.parentNode.parentNode.style.display=“none”;

}  

Now, we try again and all is well :-)

image

I haven’t tried every combination of column type to see how each render, but it is likely that there are other variations. If someone has the time, a table of this info would be handy! (Hint, hint, readers:-) )

Catch 3 - Not all controls are the same…

Now let’s create a new column called “Options” and make it a choice type type with 4 option values values of A, B, C or D.

image

Now let’s examine the above options picker in HTML (warts and all) and see if we can hide using the previous method. The key difference here is this uses multiple form elements and for some reason, the tagName is not appended to the ID attribute.

<TR>
  <TD nowrap=”true” valign=”top” width=”190px” class=”ms-formlabel”>
    <H3 class=”ms-standardheader”><nobr>Options</nobr></H3>
  </TD>
  <TD valign=”top” class=”ms-formbody” width=”400px”>
    <span dir=”none”>
    <table cellpadding=”0″ cellspacing=”1″>
      <tr>
        <td>
          <span class=”ms-RadioText” title=”A”>
          <input id=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl00″ type=”checkbox” name=”ctl00$m$g_242eacf0_89de_4c80_a74d_ec7ef12f2083$ctl00$ctl04$ctl07$ctl00$ctl00$ctl04$ctl00$ctl00″ checked=”checked” />
          <label for=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl00″>A</label>
          </span>
        </td>
      </tr>
      <tr>
        <td>
          <span class=”ms-RadioText” title=”B”>
          <input id=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl01″ type=”checkbox” name=”ctl00$m$g_242eacf0_89de_4c80_a74d_ec7ef12f2083$ctl00$ctl04$ctl07$ctl00$ctl00$ctl04$ctl00$ctl01″ />
          <label for=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl01″>B</label>
          </span>
        </td>
      </tr>
      <tr>
        <td>
          <span class=”ms-RadioText” title=”C”>
          <input id=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl02″ type=”checkbox” name=”ctl00$m$g_242eacf0_89de_4c80_a74d_ec7ef12f2083$ctl00$ctl04$ctl07$ctl00$ctl00$ctl04$ctl00$ctl02″ />
          <label for=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl02″>C</label>
          </span>
        </td>
      </tr>
      <tr>
        <td>
          <span class=”ms-RadioText” title=”D”>
          <input id=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl03″ type=”checkbox” name=”ctl00$m$g_242eacf0_89de_4c80_a74d_ec7ef12f2083$ctl00$ctl04$ctl07$ctl00$ctl00$ctl04$ctl00$ctl03″ />
          <label for=”ctl00_m_g_242eacf0_89de_4c80_a74d_ec7ef12f2083_ctl00_ctl04_ctl07_ctl00_ctl00_ctl04_ctl00_ctl03″>D</label>
          </span>
        </td>
      </tr>
    </table>
    </span>
  </TD>
</TR>

So in this form control example, there are multiple <input> tags. That’s okay, because so long as we find one that matches our criteria, we are hiding the <TR> that these controls are inside of. But as mentioned if you look at the ID and Name attributes of each control, there is no form element type appended to them.

Therefore given that a key parameter that Microsoft’s getTagFromIdentifierAndTitle() function relies on is missing, it will never find this form element and is not usable as a mechanism to hide it.

Thus the method I have described thus far has its uses, but it will not work for all controls.

What about DispForm.aspx?

Well, wouldn’t it be nice to also apply this JavaScript method to dispform.aspx. Since you have a need to hide fields from data entry fields of NewForm.aspx and EditForm.aspx, it stands to reason that you might prefer to hide the display of certain columns when a list item is displayed. Unfortunately, it is not as easy, because we are no longer dealing with form controls. There are no ID fields that have a tagName and thus this page suffers from the same problem that option buttons do. :-(

CleverWorkAround Rating: Meh

A different JavaScript approach

This begs the question as to whether there is a better way to identify the content areas that we wish to hide. Just remember that we are trying to hide the entire row of a given name. So what we need to do is find something more consistent than Microsoft’s method.

I did some investigation into this and uncovered the answer at the KWizCom team blog. All field controls (as well as data displayed in DispForm.aspx) have a HTML comment that describes the field’s name and its internal name. Below is an example with the comment formatted in green text.

<TR>
  <TD nowrap=”true” valign=”top” width=”190px” class=”ms-formlabel”>
   <H3 class=”ms-standardheader”><nobr>Device Name<span class=”ms-formvalidation”>*</span></nobr>
   </H3>
  </TD>
  <TD valign=”top” class=”ms-formbody” width=”400px”>
    <!– FieldName=”Device Name”
          FieldInternalName=”Title”
          FieldType=”SPFieldText”
       –>
    <span dir=”none”>
      <input name=”" type=”text” maxlength=”255″ id=”" title=”Device Name” class=”ms-long” /><br>
    </span>
  </TD>
</TR>

So how about we find all HTML elements with comments. (The code below was modified from the KWizCom post).

function findacontrol(FieldName) {

   var arr = document.getElementsByTagName(“!”);
   // get all comments
   for (var i=0;i < arr.length; i++ )
   {
      // now match the field name
      if (arr[i].innerHTML.indexOf(FieldName) > 0)
      {         return arr[i];      }
   }
}

So stepping through this code, you can see that we are grabbing all HTML elements that are comments. I then look for my required field name from the innerHTML property which shows the content of the selected comment element. In other words, everything up to the closing comment tag (–>).

Note also that by matching the command, the <TR> is now “closer” then it was when we were matching the form element itself. We are only 2 parentNode steps away from the surrounding <TR> tag.

So as a basic test, let’s attempt to hide the “device name”, the multi line “Comments” and the troublesome “options” field.

function hideFields() {

   var control = findacontrol(“Device Name”);

   control.parentNode.parentNode.style.display=“none”;
   control = findacontrol(“Comments”);
   control.parentNode.parentNode.style.display=“none”;
   control = findacontrol(“Options”);
   control.parentNode.parentNode.style.display=“none”;
}

Wohoo! All 3 fields hidden, baby!

image

Let’s also paste the complete JavaScript code into DispForm.aspx and see if it works there too.

Woo - freakin’ - hoo!!! Yeah, baby! Now we are rockin’! We now have a consistent code base that can be used to address the issues of hiding any of the controls, including DispForm.aspx!

image

CleverWorkArounds Rating: Sweet but not uber clever yet…

Conclusion

What the… I just demonstrated a much improved JavaScript method for hiding information presented on view and edit forms and my CleverWorkAround rating is not “pure genius”?

The answer my friends, is that this offers up some interesting possibilities for other clever things, but that will require a custom asp.net control to leverage and also a bit of a tidy up and optimisation of the code presented in this post. I lamented at the start of my post that it’s a pity that permissions cannot be applied to columns. Well, now they can… however I will leave that until my next exciting instalment :-)

Bye for now

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Netvouz
  • DZone
  • ThisNext
  • MisterWong
  • Wists
  • blogmarks
  • StumbleUpon
  • Technorati

No Tags

 

25 Responses to “More SharePoint Branding - Customisation using JavaScript - Part 1”

  1. MOSS 2007 says:

    Learn to talk to your CFO in their language - SharePoint 2007 Project Costing, Feasability, Planning and Governance …

    My good friend "Mr Cleverworkarounds" (I call him that because of his awesome blog - http:…

  2. CleverWorkarounds » More SharePoint Branding - Customisation using JavaScript - Part 6 says:

    […] Part 1 looked at how we can use JavaScript to deal with the issue of hiding form elements from the user in lists and document libraries. […]

  3. CleverWorkarounds » Free MOSS/WSS 2007 Web Part - Hide Controls via JavaScript says:

    […] full series can be found here: Part 1, Part 2, Part 3, Part 4, Part 5 and Part […]

  4. SharePoint, SharePoint and stuff : SharePoint Kaffeetasse #48 says:

    […] More SharePoint Branding - Customisation using JavaScript Part 1 […]

  5. Pham Trung says:

    Hi
    When you want to hide a field in Sharepoint, you can set Hidden property = true. (Using Object Model or Web Services)

    It’s very simple.

  6. admin says:

    Hiya

    Maybe read the other articles. WHat if you want this conditional and for a control inside a web part? Like a button on the list web part. The hidden property on the webpart will hide the whole thing. The hidden property on the field prevents data entry via the GUI, and this method allows it to be conditional

    regards

    Paul

  7. Andrew says:

    This should work on a non-customized form. What if you customized your form? The comments tag are not available anymore if you customized your form which more often we did.

  8. admin says:

    Good question. In the webpart I developed later in this series (see part 6), I hid controls via their ID attribute as well as via comment. How about adding a parentnodeoffset to that method? eg

    id:2 would find the element by ID and then set the parentnode.parentnode style to hidden?

  9. Andrew says:

    Thanks for the reply. I agree with your suggestion but, this makes sense if you have fewer controls. What if you have more controls to managed? For example about 50 fields.

    Thank you anyway.

  10. adrian moore says:

    Great post, but I have a question, and I hope it’s not too stupid. How exactly do I embed the javascript? If I open the page in sharepoint designer and add the code let’s say to newform.aspx and save it I surely create an unghosted page? Or is this an acceptable trade off for the functionality?

    Thanks in advance, and thanks for the interesting blog posting.

  11. adrian moore says:

    ah, Yes, it’s exactly that isn’t it, you add the code to the page in designer and create an unghosted page.

  12. admin says:

    Hi Adrian

    Your question (and more) was answered in the second exciting installment of this series. Check out parts 2 and 3 in particular..

    regards

    Paul

  13. Kandys says:

    Thank you for the above content. I have been looking for a way to hide a field in the new item form for a long time. I am glad I came across your article… Thank you again!

  14. Mike says:

    I use this technique to hide field from the editform.aspx. It works great, however when I upload a new document from Word(2003)the fields are not hidden. Is there a way to manage this problem?

  15. admin says:

    Unfortunately not using Javascript and it annoys me to no end. Office integration is done via infopath form, but the good news is, you can modify this infopath form using the infopath client and then re-upload it to the document library or content type.

  16. Mike says:

    Oh??? Is this an Infopathform? I did not know that. Where can I find the form? For some reason Infopath won’t open documentlibrary (where the form is located)

  17. admin says:

    Actually, I can’t be sure about Office 2003, but check this article

    http://office.microsoft.com/en-us/infopath/HA102019671033.aspx

  18. Mike says:

    After a few hours of searching and trying I’m not sure if it’s possible in office 2003. I’ve created the custom form and published it to the document library. In Word however the default document information pane is showing up….

  19. Mike says:

    Searching another week without result :-( In Office 2003 the infopath approach is not working. Does anyone have an idea to fix this problem? In Sharepoint designer? Or programming in Visual Studio? I don’t know where to start. Can someone put me in de right direction?

  20. CleverWorkarounds » Why do SharePoint Projects Fail - Part 6 says:

    […] and workarounds that I describe in the more technically focused articles of this blog (like the JavaScript stuff) always come from the philosophical perspective of leaving a environment where the next […]

  21. clinks63 says:

    hi..how about for a list that contains URL type of field..how can I remove the description field..includes the “type of description:” and the text box..by default it is in tandem with the URL field..thanks!

  22. Malay says:

    hi

    Very good stuff. Fantastic work. very good.

  23. David says:

    I tried using the code below, but it can’t seem to find the comment elements. I always get a array size of 0.

    var arr = document.getElementsByTagName(”!”);

  24. admin says:

    Which browser are you using? If Firefox, check previous comments..

  25. SharePoint 2007 - Hiding fields on NewForm.aspx and EditForm.aspx, the easy way « SharePoint Sherpa says:

    […] in SharePoint 2007 as useful as I did.  Thanks to the guys at CleverWorkarounds for writing an article on […]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


Today is: Thursday 28 August 2008 -