Skip to main content
Known Participant
April 25, 2025
Answered

Trying to make a hyperlinking script

  • April 25, 2025
  • 3 replies
  • 2912 views

Hi,

 

I'm trying to make a script where it will take highlighted text, make it into a hyperlink, format it with a character style and make the destination a page within the document, specifically the number that is the highlighted text. The usecase is an index - so I highlight 151, for example, and the script applies the character style "Hyperlink" and makes the text a hyperlink to page 151. Below is what I have so far but I'm tweaking a script I found elsewhere (source below) and I'm really struggling. I'm afraid this is not a particular strength of mine.

 

If anyone fancies digging into it and seeing if they can make it work, I'd be very grateful! 

Thanks, Eubhal

 

Script:

 

Main();
// If you want the script to be un-doable, comment out the line above, and remove the comment from the line below
// app.doScript(Main, undefined, undefined, UndoModes.ENTIRE_SCRIPT,”Run Script”);

function Main() {
//This takes the text that you have highlighted on the page
var myHighlightedText = app.selection[0].contents;

//this checks that you have selected some text (with a length greater than 1). If not it will tell you to select some text.
if(myHighlightedText.length < 1){
alert("No text has been selected");
}

//This targets the text frame which the highlighted text is within
var myTextFrame = app.selection[0].parentTextFrames[0];

//The highlighted text in Indesign is just a plain string – which is not an object in InDesign and therefore cannot be formatted via InDesign styles.
//This gets the index number from the start of the selected text
var styleStartIndex = app.selection[0].index;
//This gets the index number from the end of the highlighted text
var styleEndIndex = styleStartIndex + myHighlightedText.length-1;

//select the text by their index numbers previously defined.
var mySelection = myTextFrame.characters.itemByRange(styleStartIndex, styleEndIndex);
//apply the character style to it.
mySelection.appliedCharacterStyle="Hyperlink";

// To take the selected text and add a hyperlink to it, first define both the destination and the source.

//The below defines the source of the hyperlink to be the highlighted text “mySelection”
var source = app.documents[0].hyperlinkTextSources.add(mySelection);
//Destination”s” plural is used because we are selecting (via its name) a single hyperlink destination from the ARRAY of possible destinations.
var dest = app.documents[0].hyperlinkTextDestinations.itemByName("Destination 3");

//this applies the hyperlink referencing the source and the destination
app.documents[0].hyperlinks.add(source,dest, {name:myHighlightedText});

//the {name:myHighlightedText} section is naming the new hyperlink with the text that has been selected.

}

Original source: https://creativepro.com/topic/indesign-scripting-hyperlinks/

Correct answer m1b

Thanks Mark. That's a shame but I'm afraid I don't know which ones they are and as I didn't originate the file, I'm not comfortable deleting them/unused destinations. I truly don't need it to be done in one fell swoop - I have to go check each link anyway so I'm fine highlighting a number and just having a script copy the text and stick it in to the hyperlink destination as a page ref and just running that for each number. Right now, I've literally got a programmable keypad set up doing the following:

button 1: copy

button2: create new hyperlink

button 3: paste

button 4: enter

 

Which is grand but pressing one button that does all that per entry would be great, if that's possible. If the hidden destinations would still get in the way of that or if you've just had enough, that's totally fine. Thank you so much for your time and help either way.

 

Eubhal


Hi @Eubhal okay I see, well I'll just remove the part that tries to re-used an existing page destination and it will create a new one each time (same as your manual way). I'll leave the other script up though, because it is a better approach. So using your workflow, just select text containing one or more numbers and it will create hyperlinks for them. So you could select just the "1" or Apple 1,3,5 or the whole line to create 3 hyperlinks at once.

- Mark

/**
 * @file Add Hyperlinks For Page Numbers 4.js
 *
 * Usage:
 *   1. Select the existing "index" text
 *      - can be a text frame or some selected
 *        text or a whole story
 *
 *   2. Run script
 *
 * @author m1b
 * @version 2025-05-09
 * @discussion https://community.adobe.com/t5/indesign-discussions/trying-to-make-a-hyperlinking-script/m-p/15288981
 */
function main() {

    var settings = {
        hyperlinkCharacterStyleName: 'Hyperlink',
    }

    var doc = app.activeDocument,
        target = doc.selection[0];

    if (
        undefined == target
        || 'function' !== typeof target.findGrep
    )
        return alert('Please select the index text and try again.');

    var characterStyle = getThing(doc.allCharacterStyles, 'name', settings.hyperlinkCharacterStyleName);

    if (!characterStyle)
        return alert('Could not find "' + settings.hyperlinkCharacterStyleName + '" character style.');

    // clear the find/change grep preferences
    app.findGrepPreferences = NothingEnum.nothing;
    // set the find options
    app.findChangeGrepOptions.includeFootnotes = false;
    app.findChangeGrepOptions.includeHiddenLayers = false;
    app.findChangeGrepOptions.includeLockedLayersForFind = false;
    app.findChangeGrepOptions.includeLockedStoriesForFind = false;
    app.findChangeGrepOptions.includeMasterPages = false;

    // set the grep to search for page numbers or ranges (~= is endash)
    app.findGrepPreferences.findWhat = '\\d+([~=-]\\d+)?';

    // perform the find
    var found = target.findGrep();

    for (var i = 0; i < found.length; i++) {

        var text = found[i],
            parts = text.contents.split(/[\u2013-]/g),
            entries = [];

        // add the first page number
        entries[0] = {
            text: text.characters.itemByRange(0, parts[0].length - 1),
            pageName: parts[0],
        };

        if (
            2 === parts.length
            && Number(parts[1]) < Number(parts[0])
        ) {

            var secondPageName = parts[0].slice(0, parts[0].length - parts[1].length) + parts[1];

            // expand the second page number, if present
            entries[1] = {
                text: text.characters.itemByRange(text.contents.length - secondPageName.length + 1, secondPageName.length + 1),
                pageName: secondPageName,
            };

        }

        for (var j = 0; j < entries.length; j++) {

            var page = getThing(doc.pages, 'name', entries[j].pageName);

            if (!page)
                continue;

            // remove existing hyperlink
            var existing = text.findHyperlinks();
            for (var k = existing.length - 1; k >= 0; k--)
                existing[k].remove();

            var source = doc.hyperlinkTextSources.add(entries[j].text.characters.itemByRange(0, entries[j].text.length - 1)),
                destination = doc.hyperlinkPageDestinations.add(page, { hidden: true });

            // give the hyperlink a useful name
            var name = entries[j].text.paragraphs[0].words[0].contents,
                n = 0;

            // a name that won't conflict
            while (doc.hyperlinks.itemByName(name + ' ' + (++n)).isValid);

            // create the hyperlink and name it
            doc.hyperlinks.add(source, destination).name = name + ' ' + n;

            // apply the character style
            entries[j].text.applyCharacterStyle(characterStyle, true);

        }

    }

};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Add Index Hyperinks');

/**
 * Returns a thing with matching property.
 * If `key` is undefined, evaluate the object itself.
 * @author m1b
 * @version 2024-04-21
 * @param {Array|Collection} things - the things to look through.
 * @param {String} [key] - the property name (default: undefined).
 * @param {*} value - the value to match.
 * @returns {*?} - the thing, if found.
 */
function getThing(things, key, value) {

    for (var i = 0; i < things.length; i++)
        if ((undefined == key ? things[i] : things[i][key]) == value)
            return things[i];

};

Edit 2025-04-28: Added ability to correctly match page number ranges, eg. 34-6 and ignore the second part.

Edit 2025-04-29: Added handling of en dash or hyphen between page numbers

Edit 2025-05-09: Fixed bug due to using a javascript regex in find grep, oops! Fixed bug due to using wrong code for endash. I must have been asleep! Added logic for "expanding" second number in page range so that 12–3 will create hyperlinks to pages 12 and 13 respectively.

3 replies

Frans v.d. Geest
Community Expert
Community Expert
April 25, 2025

That is exactly why InDesign has Cross References(!).

EubhalAuthor
Known Participant
April 25, 2025

I would still need to set each reference up manually though, right?

Robert at ID-Tasker
Legend
April 25, 2025

@Eubhal

 

This code is so wrong on so many levels 😞 

 

And Cross-References would be much better - they would update automatically when text shifts. 

 

EubhalAuthor
Known Participant
April 25, 2025

I mean, I'm sure they tried their best and so did I. That's why I'm asking for help here. I've not used cross-reference before but from a cursory glance it doesn't seem to do what I'm looking for (add a hyperlink to highlighted text) and definitely doesn't seem to automate the process so I'm not sure it's the correct route for me. Feels like a script should be able to accomplish this, I just need some help with that.

Thanks,

 

Eubhal

Robert at ID-Tasker
Legend
April 25, 2025

@Eubhal 

 

I'll take a look at your files later today.

 

m1b
Community Expert
Community Expert
April 25, 2025

Hi @Eubhal this looks like it might be a better use for Cross References. They have the advantage that, even if the pages change, the links will still follow them. In any case, could you create a small sample .indd document and post here for us to look at—maybe one before and one after the script is run. That is a good way to make your expectations clear.

- Mark

EubhalAuthor
Known Participant
April 25, 2025

Thanks m1B! I'm afraid the script is not currently running without errors so no after file is possible yet. I can make up something to show what I'm working on though. Cross-references looks a lot more complex than what I'm needing for this - the references won't change later, the main issue is that there are a lot of entries so I'm trying to automate as much as I can per entry - preferably just highlight, run script, done. It occurs to me that it's essentially the same as the Convert URLs to hyperlinks feature but that the destination needs to be page rather than url and that that feature only works on text it has found but fundamentally the same idea. I'll make up an example file and comment again with it. Thanks again for your time!

Eubhal

m1b
Community Expert
Community Expert
April 25, 2025

@Eubhal just to clarify—yes it does sound like you could use some automation here. The only question is the best way to go about it. Cross References actually have hyperlinks as part of them (when you make them programatically you need to create a hyperlink). When I asked for a sample document, I don't mean *after* your script is run, I mean a page (or two if necessary) of manually created hyperlinks to show what you expect a hypothetical script to produce. Mainly it needs to show what the source text looks like. It sounds like you are saying the source text is just a number. Are they set in a character style or anything, or do you just need to find them by hand?

- Mark