Getting the ID of Content Editor & Script Web Parts the RIGHT way

Since SharePoint 2007 many people have been using Content Editor Web Parts to provide customisations in their SharePoint solutions.  This has always been a very powerful and simple way to achieve results, either by embedding fragments of CSS, JavaScript or HTML directly (not always ideal when the markup gets garbled on save!), or by referencing a hosted file stored elsewhere – in the Style Library for example.

Drawback

This is fine for quick and dirty adjustments, or UI enhancements with jQuery and the like.  But sometimes you might have written some JavaScript specific to a web part, but then find you need to have more than one instance of the same web part on the page.  You have your markup and JavaScript referenced from a single file, and add two instances of it to the page – and voilà! It’s not doing what it should be doing. All the IDs and classes you referenced are now duplicated!

Fiddlesticks!

Draw Forward

Actually, when any web part is added to the page it is given a unique ID by the framework.  This ID is the ID attribute of the web part’s container element. What if you could get a hold of this specific element? This is what you need to ensure you select the correct web part from your code and update the right parts of the page.

Luckily every script block in the page added via a CEWP or Script web part can be picked up while the page is loading. At this point in time we can get a reference to the web part container it resides in and, once the page has completed loading, return it. Then we will be ready to execute our web part initialisation armed with a reference to the right bit of the page to either inject content or make jQuery selections upon.

The following function can be added to the master page to perform this task when called upon.

function _initWebPartOnLoad(cbOnLoad) {
    closest(document.getElementsByTagName('script')[document.getElementsByTagName('script').length - 1], 'ms-WPBody', function (el) {
        if (el) {
            var fnName = 'startWebPart_' + el[0].id;

            window[fnName] = function () {
                cbOnLoad(el);
            }
            _spBodyOnLoadFunctionNames.push(fnName);
        }
    });

    function closest(el, classname, cb) {
        var newEl = el.parentNode.getElementsByClassName(classname);

        if (newEl.length)
            cb(newEl);
        else {
            el.parentNode && closest(el.parentNode, classname, cb) || cb();
        }
    }
}

So then, for our web part implementation, we call the above function from a script tag in our CEWP, or from a js file we have statically linked (in a CEWP or Script web part).

    function onLoadMyWebPart(webpart) {
        var $webpart = $(webpart);
        $webpart.append('<h1>My Web Part ID is ' + $webpart.attr('id') + '</h1>');
    }

    _initWebPartOnLoad(onLoadMyWebPart);

Notice a callback function is supplied. This callback is added to the array of functions that SharePoint will execute after the rest of the page has loaded.  When it is called the web part will be initialised, and it takes one argument – the web part element from the DOM!

So now you are ready to go – and you have reference to the correct instance of the web part.

Lovely.