views:

4718

answers:

3

I'm using Sharepoint (WSS 3.0), which is unfortunately very limited in its ability to format survey questions (i.e., it strips any HTML you enter). I saw a solution elsewhere that suggested we add some JS to our master page file in order to allow line breaks. This works beautifully, but I'd like to see if we can allow links as well.

In our WSS surveys, I can now use {{br}} anywhere I want a line break (this works). I have tried extending the code to allow the use of link tags (e.g., {{link1}}url{{link2}}URL Title{{link3}}; but, this doesn't work, presumably because the updates aren't happening as a whole, and the browser then tries to render it piece by piece, confusing it. (FF and IE show different results, but both fail. If I mix up the order of the JS below -- i.e., do link3, 2 and then 1 -- the output changes as well, but still fails.) Is there a better way to do this?

Thanks!


<script language="JavaScript">
var className;
className = 'ms-formlabel';
var elements = new Array();
var elements = document.getElementsByTagName('td');
for (var e = 0; e < elements.length; e++)
{
if (elements[e].className == className){
elements[e].innerHTML = elements[e].innerHTML.replace(/{{br}}/g,'<br/>');
elements[e].innerHTML = elements[e].innerHTML.replace(/{{link1}}/g,'<a href="');
elements[e].innerHTML = elements[e].innerHTML.replace(/{{link2}}/g,'">');
elements[e].innerHTML = elements[e].innerHTML.replace(/{{link3}}/g,'</a>');}
}
</script>
+3  A: 

Instead of modifying the innerHTML property in chunks (the browser tries to update the DOM each time you change innerHTML, which if you provide incomplete/broken markup, will obviously mess things up), do all your modifications against your own string variable, and then overwrite the entire innerHTML with your completed string:

<script language="JavaScript">
var className;
className = 'ms-formlabel';
var elements = new Array();
var elements = document.getElementsByTagName('td');
for (var e = 0; e < elements.length; e++)
{
if (elements[e].className == className) {
    var newHTML = elements[e].innerHTML;

    newHTML = newHTML.replace(/{{br}}/g,'<br/>');
    newHTML = newHTML.replace(/{{link1}}/g,'<a href="');
    newHTML = newHTML.replace(/{{link2}}/g,'">');
    newHTML = newHTML.replace(/{{link3}}/g,'</a>');}

    elements[e].innerHTML = newHTML;
}
</script>
Rex M
This makes sense, but it sounds like I'd then need to pull the URL from between the {{link1}} and {{link2}} tags and the title from between the {{link2}} and {{link3}} tags in order to form the entire link and replace everything at once. Pardon my ignorance, but do you know how I would accomplish this?
Mark
innerHTML is essentially nothing more than a string, so all you're doing with newHTML is manipulating a string. It is only parsed and put into the DOM once you manipulate the element's innerHTML.
Daniel Lew
Okay, I think I understand at least at a high level. (And thank you to everyone for your quick responses.) When I add in the code above/below, I end up with a handful of "undefined" pieces of text all over my WSS pages (on all pages, not just ones with {{link}} tags). It seems to have something to do with 'var newHTML = elements[e].innerHTML;', as if I comment that line out the errors go away (but of course, nothing is then replaced). Any ideas?
Mark
@Mark my code and your code are doing exactly the same thing. In this particular example, you should not need to pull anything from anywhere, assuming you write your tokens such that {{link1}} and {{link2}} always wrap the URL. It will not support more robust cases like @Daniel's second example, but it will get the job done.
Rex M
@Mark you should check to see that elements[e] actually exists and has children before you try to do anything with it.
Rex M
Hah. I was the one that messed up. I now have this working beautifully. Rex, Daniel -- THANK YOU. This is awesome. Much, much appreciated.
Mark
+1  A: 

The simple answer would be to build up the innerHTML and replace it all at once:

for (var e = 0; e < elements.length; e++)
{
    if (elements[e].className == className) {
        var newHTML = elements[e].innerHTML;
        newHTML = newHTML.replace(/{{br}}/g,'<br/>');
        newHTML = newHTML.replace(/{{link1}}/g,'<a href="');
        newHTML = newHTML.replace(/{{link2}}/g,'">');
        newHTML = newHTML.replace(/{{link3}}/g,'</a>');
        elements[e].innerHTML = newHTML;
    }
}

The more complex answer would be to use capturing groups in your regex and pass a function as the 2nd parameter to replace(), so as to use a single call to replace() for the HTML. For example,

elements[e].innerHTML = elements[e].innerHTML.replace(/({{link1}})|({{link2}})|({{link3}})/g, 
    function (match) {
        var map = { 
            '{{link1}}' : '<a href="',
            '{{link2}}' : '>',
            '{{link3}}' : '</a>', }
        return map[match];
    });

The second solution is more complex and leads to some ugly regexes, but is more efficient than calling replace() over and over again.

Daniel Lew
Funny, we wrote nearly exactly the same code at the same time.
Rex M
Sort of, I posted the simple answer then worked on the more complex answer in the meantime.
Daniel Lew
A: 

This article is useful...but my difficulty is i hv to replace a a row on perticular row.id using innerHTML ...how to do that...using object.innetHTML replaces entire column that i dont want..plz help