Dealing with character offsets

My current setup of a webpage is like this, I have a text area where a paragraph content is present. This paragraph has some key words in it. I have another one column table which has list of key words which are present in the paragraph.

My goal is to have those words present in the paragraph surrounded by a border when a user clicks on the words present in the one column table. Also if the word is present somewhere at the end of the paragraph , upon user click, it should automatically scroll down to that location with border around it.

Options I am exploring:

I was looking at Rangy library and saw that they have used Number startOffset and Number endOffset. I believe they are first obtaining these values based on user’s selection and in my case, I already have those values.

I am wondering based on my scenario, can I use Rangy to achieve my goal? Or are there any other libraries I should consider looking at? Anyone aware of any example matching my scenario?

Thanks

This could be done very simply using html anchors linking to IDs on the the words. Then use the :target pseudo class in css to apply the border.
No need for scripting or libraries.

I’m not sure if this is an over simplification of your requirements or I’m missing something, but this does like you describe, highlighting a clicked word with a border and scrolling to it.

1 Like

Thanks for your reply and sharing the link. The words Chickens, Fish and Weasels have IDs associated with them which unfortunately I don’t have. I just have JSON data ( Please refer to this post of mine for the sample JSON data I have with start and stop values). Hence, I believe I am only left with using libraries option? Let me know if I can explain more.

Hey @Jack_Tauson_Sr, it might be particularly worth noting that you’re using a jqxPanel here; so you don’t want to scroll the document, but the panel. ;-) Now there’s a built in method for this; the adjusted code from your previous thread might then go like

var panel = $("#jqxPanel");
var content = panel.html();

panel.jqxPanel({
  width: 450,
  height: 120
});

$("#jqxgrid").on("cellclick", function(event) {
  var value = event.args.value;
  
  // Use a regular expression to account for the beginning/end of the
  // input, arbitrary whitespace (including line feeds) as well as
  // adjacent tags
  var regExp = new RegExp('(^|>|\\s)(' + value + ')($|<|\\s)', 'g');
  
  var scrollTop;

  var highlighted = content.replace(
    regExp,
    '$1<span class="highlighted">$2</span>$3'
  );

  panel.jqxPanel('clearcontent');
  panel.jqxPanel('append', highlighted);
  
  // Get the offset of the last highlighted word relative
  // to the panel parent
  scrollTop = $('.highlighted:last', panel).position().top;
  
  // Use the jqxPanel API to scroll to that word
  panel.jqxPanel('scrollTo', 0, scrollTop);
});

I’ve also used a regular expression instead of a string for the replace pattern to account for those other points you mentioned over there, such as preserving the formatting when wrapping the content in <pre> tags. Here’s a fiddle.

I don’t think that rangy library will help you here BTW – those offset values will become useless after the first replacement anyway.

Hey @m3g4p0p , Thanks very much for your answer. You are right, it’s jqxPanel and not document. I have a slight change in my requirement that I would like to discuss with you. Actually, there is another jqxGrid present in my web application , so when a user clicks on it, I see the jqxGrid and jqxPanel simultaneously on my web page. The jqxGrid and jqxPanel I am taking about are the one we have been working on. So, basically, I will already be having all of the words listed in Concepts column of jqxGrid highlighted in the paragraph content of jqxPanel. So, when a user clicks on the words present in the Concepts column of jqxGrid,it will just surround those specific words in the jqxPanel paragraph with a black border( Please note: the word has already been highlighted before in this scenario). Do you think that the offsets values still won’t come into picture here?

Since, right now, upon clicking the cell value of jqxGrid column, all the words present in the document are getting highlighted, there can be scenario as far as the content of the jqxPanel is concerned, when a user clicks on the jqxGrid cell value, I will need to highlight only certain words and not all the words and I believe then the start and stop values will come into play. Do you think we can make use of that somehow?

Hey @m3g4p0p

I get uncaught typeerror cannot read property 'top' of undefined error when I run your working code in my application. It works fine in JSFiddle though. I can understand that the line
scrollTop = $('.highlighted:last', panel).position().top; is unable to find the highlighted class as console.log ("Check for highlighted in click handler:" +highlighted);
doesn’t shows highlighted class getting applied to the document in my webapp.

Is this because, I am adding the following code:

for (var i = 0; i < length; i++) {
    
                      html = html.replace(new RegExp(records[i].concept_text, 'ig'), '<span style="color:' + color + ';">' + records[i].concept_text + '</span>');
                    }

and when I check the console.log("How many span tags you are noticing here?: " + html);, I see the words (concept_text) are surrounded by two span tags like the following:

MYELOMA

And somewhere I noticed even three span tags.

Could it be the reason, the regular expression you mentioned var ``` highlighted = content.replace( regExp, '$1<span class="highlighted">$2</span>$3' );

				  
Thanks




this.myDocument = function (data_) {

        var source = {
            localdata: data_,
            datafields: [{
                name: 'doc_content',
                type: 'string'
            }, {
                name: 'concept_text',
                type: 'string'
            }, {
                name: 'start',
                type: 'int'
            }, {
                name: 'stop',
                type: 'int'
            }],
            datatype: "array"

        };


        var dataAdapter = new $.jqx.dataAdapter(source, {

            loadComplete: function (records) {
                var html;
                var color = '#FF0000';

                //Get data
                var records = dataAdapter.records;
                var length = records.length;
                console.log("Checking Length: "+length);// Outputs 5 
                for (var i = 0; i < length; i++) {
                console.log("checking Concepts here: " +records[i].concept_text);
                }
				html = "<div style='margin: 10px;'><pre>" + records[1].doc_content + "</pre></div>";
				
                for (var i = 0; i < length; i++) {

                  html = html.replace(new RegExp(records[i].concept_text, 'ig'), '<span style="color:' + color + ';">' + records[i].concept_text + '</span>');
                }
                 console.log("How many span tags you are noticing here?: " + html);
                $("#docContentPanel").html(html);
            },
            loadError: function (xhr, status, error) { },
            beforeLoadComplete: function (records) {

            }
        });
        // perform data binding
        dataAdapter.dataBind();

        
        $("#myPanel").jqxGrid({

            source: dataAdapter,
            width: '122',
            height: '170',

            columns: [{
                text: 'Concept(s)',
                datafield: 'concept_text',
                cellsalign: 'center',
                width: 100
            }, {
                text: 'Note Content',
                datafield: 'doc_content',
                hidden: true
            }

            ]
        });

        var panel = $("#docContentPanel");
        
        var content = panel.html();

      

        panel.jqxPanel({
            width: '750',
            height: '500',
            scrollBarSize: 20
        });


        $("#myPanel").on('cellclick', function (event) {

           
              
            var value = event.args.value;
  
			  // Use a regular expression to account for the beginning/end of the
			  // input, arbitrary whitespace (including line feeds) as well as
			  // adjacent tags
			  var regExp = new RegExp('(^|>|\\s)(' + value + ')($|<|\\s)', 'g');
			  
			  var scrollTop;

			  var highlighted = content.replace(
				regExp,
				'$1<span class="highlighted">$2</span>$3'
			  );
			  
			  console.log ("Check for highlighted in click handler:" +highlighted);
			  panel.jqxPanel('clearcontent');
			  panel.jqxPanel('append', highlighted);
			  
			  // Get the offset of the last highlighted word relative
			  // to the panel parent
			  scrollTop = $('.highlighted:last', panel).position().top;
			  
			  // Use the jqxPanel API to scroll to that word
			  panel.jqxPanel('scrollTo', 0, scrollTop);


            


        });

    }; // End of myDocument function

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.