Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create URL with anchor to text selection

I would like to create a URL pointing to a web service I write, http://mycoolthing.com, together with an #-anchor that refers to a text selection on that page.

Is is possible in HTML/JS to create an anchor that refers to a Selection or a RangeObject?

like image 946
Nico Schlömer Avatar asked Dec 26 '22 00:12

Nico Schlömer


2 Answers

chromium based browsers now support Scroll To Text Fragment

https://chromestatus.com/feature/4733392803332096#:~:text=This%20feature%20allows%20a%20user,and%20scrolls%20it%20into%20view.

like image 163
jcdietrich Avatar answered Jan 05 '23 17:01

jcdietrich


As I mentioned in a comment, here is an option to select the text by using JavaScript and the hash (#) on the url.

It is divided in two parts:

  1. The calling page will have a link to the target page (they can be the same) with a # indicating the text that you want to select (or if you know it, the ID of the element):

    <a href="myPage.html#Lorem">Link to my page with Lorem select</a>

  2. The target page will have some JavaScript code that will select the text indicated in the # (if the hash is a valid element id, it will select the text within that element instead of the text).

*Notice that for this solution I used some code from Selecting text in an element (akin to highlighting with your mouse) (Kimtho6's solution)

/**
 * SelectText: function from https://stackoverflow.com/questions/985272/selecting-text-in-an-element-akin-to-highlighting-with-your-mouse (Kimtho6 solution)
 * 
 * element: string that contains an element id (without the #)
 */
function SelectText(element) {
    var doc = document;
    var text = doc.getElementById(element);    
    if (doc.body.createTextRange) { // ms
        var range = doc.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if (window.getSelection) {
        var selection = window.getSelection();
        var range = doc.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);

    }
}

/**
 * selectHashText: function that selects the first appearance of a text (or an element with the id) indicated in the location hash
 */
function selectHashText() {

    // check if there's an element with that ID
    if ($(document.location.hash).length == 0) {

        // select some text in the page
        var textToSelect = document.location.hash.substring(1);

        // if it's not an empty hash
        if (textToSelect != "") {

            // iterate the different elements in the body
            $("body *").each(function() {

                // if one contains the desired text
                if ($(this).text().indexOf(textToSelect) >= 0) {

                    // wrap the text in a span with a specific ID
                    $(this).html(
                        $(this).html().replace(textToSelect, "<span id='mySelect'>" + textToSelect + "</span>")
                    );

                    // select the text in the specific ID and stop iteration
                    SelectText("mySelect");
                }
            });
        }

    } else {
        // select the content of the id in the page
        SelectText(document.location.hash.substring(1));
    }
}

// execute the text selection on page load and every time the hash changes
$(window).on("hashchange", selectHashText);
$(document).ready(function() { selectHashText(); });

I created this jsfiddle, but it doesn't take the # so it doesn't help much. You can also see it on this web page. Notice that is the same code as in the jsfiddle (and above), just on one page.


UPDATE: (Elaborated from the comments below)

Without using the hash, you can pass the text that you want to select as a parameter in the GET or POST (I will use GET for simplicity):

<a href="myPage?select=Lorem">Select Lorem</a>

Then using your back-end language (I used PHP, you could use any other or even JavaScript parsing the location href), save the text into the textToSelect variable that I used before. As a last step, replace the .text() for .html() that I used (so the tags are included in the search too)

/**
 * gup: function from https://stackoverflow.com/questions/979975/how-to-get-the-value-from-the-url-parameter (Haim Evgi's solution)
 * 
 * name: string that contains the name of the parameter to return
 */
function gup( name ) {
    name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
    var regexS = "[\\?&]"+name+"=([^&#]*)";
    var regex = new RegExp( regexS );
    var results = regex.exec( window.location.href );
    return results == null ? null : results[1];
}

/**
 * selectHashText: function that selects the first appearance of a text (or an element with the id) indicated in the "select" parameter
 */
function selectTextAcross() {

    // get the "select" parameter from the URL
    var textToSelect = decodeURIComponent(gup("select"));

    // if it's not an empty hash
    if (textToSelect != "") {

        // iterate the different elements in the body
        $("body *").each(function() {

            // if one contains the desired text
            if ($(this).html().indexOf(textToSelect) >= 0) {

                // wrap the text in a span with a specific ID
                $(this).html(
                    $(this).html().replace(textToSelect, "<span id='mySelect'>" + textToSelect + "</span>")
                );

                // select the text in the specific ID and stop iteration
                SelectText("mySelect");
            }
        });
    } 
}

$(document).ready(function() { selectTextAcross(); });

You can see it working on this web page.

About this solution:

  • It works (hey! better than nothing! :P)
  • You can include html tags (you cannot with the # one)
  • You need to know the document's source
  • The generated code may not be valid and then the result may not be the expected one (all the browsers I tested with, they stop selecting after the first "crossed" tag)
like image 22
Alvaro Montoro Avatar answered Jan 05 '23 17:01

Alvaro Montoro