Search code examples
javascriptms-wordoffice-jsword-contentcontrol

Office.js get range adjacent to a content control


I have the following scenario Word:

  • text document with some content controls (also text)
  • content controls are always adjacent to normal text (without a space between the text and the content control)
  • content controls have the same style as the adjacent text when they are created (by editing their font.color, font.name, font.size etc. which is read from the selection in which the content control is being inserted)
  • when content controls are updated (their content is updated), their style is reset and I need to apply the document style again (not an issue, works fine)

The issue is, when content controls are updated, the current document selection can be anywhere but I need to be sure they re-apply the same style as the text adjacent to them.

Is there a way inside Office.js to get the range adjacent to a content control? That way I could read it's style and apply it to the content control.


Solution

  • My assumption is that you want the text immediately preceding the content control. Unfortunately, the object model doesn't provide an equivalent to getNextTextRange for going "backwards" (getPreviousTextRange).

    It can be done, but it's a bit round-about. Following is my Script Lab sample code:

    async function
        getAdjacentRange() {
        await Word.run(async (context) => {
    
            var ccs = context.document.contentControls;
            ccs.load("items");
            await context.sync();
            console.log("Nr cc: " + ccs.items.length);
            let cc = ccs.items[0];
    
            //The starting point of the content control so that 
            //the range can be extended backwards          
            let ccRange = cc.getRange("Start");
            let ccParas = ccRange.paragraphs; cc.load("text,range,paragraphs");
            let ccPara = ccParas.getFirst();
            ccParas.load("items")
    
        //The content control must be in a paragraph: get that paragraph's
        //starting point so that the range can be extended in that direction
            let paraRange = ccPara.getRange("Start");
            let rng = paraRange.expandTo(ccRange);
       //Get the words in the range of the start of the paragraph to the 
       //start of the content control in order to get the last one before the content control
            let em = [" "];
            let words = rng.getTextRanges(em, true);
            words.load("items");
            await context.sync();
            let nrWords = words.items.length;
    
          //minus 2 to get second to last word since last word is directly adjacent to content control (no space)
            let lastWord = words.items[nrWords - 2];
    
            //Now get the content from the end of the second to last word to
            //the start of the content control, which will be the last word
            let word2BeforeCC = lastWord.getRange("End");
            let wordBeforeCC = word2BeforeCC.expandTo(ccRange);
            wordBeforeCC.load("text");
            await context.sync();
            console.log(cc.text + "/ Word before the content control: " + wordBeforeCC.text + " / nr Words: " + nrWords);
    })
        }
    

    Code for the case when there is a space between the last word and the content control:

    async function
        getAdjacentRange() {
        await Word.run(async (context) => {
            var ccs = context.document.contentControls;
            ccs.load("items");
            await context.sync();
    
            let cc = ccs.getFirstOrNullObject();
    
            //The starting point of the content control so that 
            //the range can be extended backwards
            let ccRange = cc.getRange("Start");
    
            //The content control must be in a paragraph, so get that paragraph's
            //starting point so that the range can be extended in that direction
            let ccParas = ccRange.paragraphs; cc.load("text,range,paragraphs");
            let ccPara = ccParas.getFirst();
            ccParas.load("items")
            let paraRange = ccPara.getRange("Start");
            let rng = paraRange.expandTo(ccRange);
    
            //Now it's possible to get the last word in the extended range
            let em = [" "];
            console.log(em.length.toString());
            let words = rng.getTextRanges(em, true);
            words.load("items");
            await context.sync();
            let nrWords = words.items.length;
            //returns a Range object, from which you can get the style info
            let lastWord = words.items[nrWords - 1];
    
            await context.sync();
            console.log(cc.text + "/" + "nr Words: " + nrWords + "last word: " + lastWord.text);
    })
        }