Find and scroll to (or highlight) some text on page - javascript

I want to find text on page and scroll to it. Eg finds the first word of the match and injects hidden element next to it with the purpose of scrolling to that point.
Here's the problem: text might span multiple paragraphs, and some words of the "needle" text might be represented by links, headers, etc.
Not sure how to start tackling this, any ideas?

https://github.com/afeld/bootstrap-toc/blob/gh-pages/dist/bootstrap-toc.js
Might not seem related on first glance but if you dig into how the headings are searched for and selected you might be able to adapt it for your "find text on page" use case.
Creating a hidden element that you can then scroll to is already mostly implemented in this project as well so hopefully you find this useful.

Related

Why is the bounding rectangle of the selection range wider than the actual text selection?

I need to get the bounding rectangle of the text selection. I am using this code:
if (window.getSelection && window.getSelection().rangeCount) {
var range = window.getSelection().getRangeAt(0).cloneRange()
if (range.getBoundingClientRect && range.getBoundingClientRect()!=null)
return range.getBoundingClientRect()
}
But it often returns a rectangle that is much wider than the text selection, even though every element of the selected text doesn't extend that wide.
For example, when I select the text of the fist two paragraphs of https://simple.wikipedia.org/wiki/Gluon the rectangle includes the picture to the right.
It seems that the problem occurs every time when the paragraph boundary is inside of the text selection, and this makes the rectangle to extend to the paragraph width.
How to solve this problem?
The image being included in the multi-paragraph selection may be a problem for what you want to accomplish or the way you want to interpret the document structure--yet isn't "wrong" from the point of view of the HTML/CSS layout model or the browser's layout engine.
Simplifying the document structure:
<div id="nw-content-text">
<div style="float: right">
<img src="gluons.png">
</div>
<p>From Wikipedia...</p>
<p>Gluons are what hold quarks together...</p>
...
</div>
In Chrome e.g., inspect the page. Then in the Elements view select either of the <p> tags. The rendered view will show highlighted background extending across the image area:
That's because the image floating to the right is indeed consuming part of the paragraph's maximum possible bounding box. If you want to consider just paragraph text, that feels wrong, but it's how the CSS layout model works. Depending on your font sizes and such, you may be able to select the third para as it flows underneath the image, and it indeed flows to the same far right edge.
Given the float: right of the image's container, the browser "thinks" the image and its container <div>s "lives" to the right of the first para, but before the second. That is its logical "attachment point" or "anchor point." Select the first para, and the browser knows the image isn't included. Select the second, and likewise. Select the combination, though, and the image is correctly included because the anchor point lives between the two paragraphs. Ask for the bounding box of either para alone and the browser responds with a tight box. But ask for tbe bounds of the selection that spans those paras, and it must include the anchor point and contained image that anchor point represents. The much larger bounding box including the image is the right answer.
Okay, so much for the layout theory explaining why this happens as it does. Now how to get it to do what you want it to do, and exclude the contained image from the selection bounding box?
Given that CSS and the browser doesn't interpret the selection the way you want, you won't be able to use getBoundingClientRect() on the selection directly. However, you can navigate from the selection to the nodes included, and filter out any tags that you don't want. In this case, you seem to only want <p> tags, which are easy enough to filter for. Compute the bounding box of each para in your selection, then return a box that is the union of them.
Beware that working with ranges is a bit tricky, especially as browsers historically had different models for how to manage them. A cross-browser library like rangy might help, though simply navigating from a range to the underlying nodes probably doesn't require that much extra framework. There are quite a few articles on Stack Overflow about "getting from JavaScript selection to underlying nodes," with many code samples. They're not all simple...but just filtering for paras is a fairly simple case. Or if you don't want to dive into all the range issues, you can use something like Selection.containsNode() to less elegantly but still quickly iterate through possible nodes and see if they're in the selection.
Also beware: Your example document makes things simple by just needing filtering for paras, and by having just one floating item to exclude. But the variability across all HTML / CSS documents is enormous. That variability can make solutions fragile. E.g. if sometimes you need to filter for more than just <p> elements, or need to filter into the middle of text nodes not affiliated with a tightly-wrapped tag, things quickly get more dicey. The variations and the different edge cases will complexify your reinterpretation of what a selection's bounding box should enclose (and should ignore).

CKeditor - move cursor to next container when reached end of the first one

Looking for some solution to my big big problem with text edit. Short description below:
Lets say I have two (or more) editable containers. What I need is when you typing on a first container and the cursor reaches end of it, instead expanding it i want it to move to next editable container, it is like in regular Word document, when you reach end of the page it takes cursor to the next page, i need the same thing on two div containers, looking for tips solutions. Is it doable ?
You really don't want to use plain contentEditable :D. And it won't help - it will be exactly the same situation. You're looking for a holy grail of editors based on contentEditable - how to split content into multiple pages. It's doable using the CSS regions, but only Safari IIRC supports them. Thus, we're keeping fingers crossed for this spec :).

Grabbing the sentence that a selected word appears in

Using Javascript, I need to allow a user to double click a word on a page and retrieve the sentence that it appears in. Not just any sentence, but that specific one. I've toyed with retrieving all sentences that that word appears in and somehow choosing the correct sentence, so maybe that's an option.
I've scoured the web looking for this beast, and I've thought a lot about it. Some have recommended using Rangy but I haven't been able to find the functionality I'm looking for, or even functionality that would help me get where I need to be.
Any ideas?
You could turn your page into one or multiple read-only textareas, use clever CSS styling to mask it, then use the onselect event as described here: Detect selected text in a text area with javascript
Depends of course, how your page looks like and where it's used.

Mentioning system that mimics Facebook's

I've had a horrible problem that I've been wracking my brain for the past two days for, and have yet to come up with a solution. As such, I think this needs someone smarter than I to accomplish.
What I'm trying to build is a textbox that simulates that of Facebook's; essentially, the tagging function.
Now if you've used Facebook, you'll have noticed that Facebook allows you to tag people in a comment/post, simply by typing in their name and selecting from a dropdown list. The name of the person you've selected then appears in highlighted text in that very textarea. I've successfully managed to create and populate the dropdown list a combination of JQuery and AJAX, but the tagging process itself is the stumper.
Once a dropdown item has been selected (by Enter or clicking), the query text will be replaced with the tagged name. Now, it's difficult to see how one can give text in a textarea any kind of a highlight, so I've discovered (by inspecting elements in Google Chrome and deleting the textarea node) that the textarea itself is transparent, and there is a white div below "simulating" the text. Highlighted words are placed in a tag with custom CSS, which gives it that blue background. All of this I've found out myself, and I have successfully simulated this - but I can only do one tag.
Now I've investigated further and found an input type="hidden" element, of class "mentionsHidden". This input element has a value attribute, which dynamically populates itself based on the content of the textarea. So if I typed "ABC", the value of the element becomes "ABC". If I included a tag, say "hi [Rei]!" (where the name in [] is the tag), the value of the element becomes "hi #[member_id:Rei]!".
So I HAVE done my homework. But here comes the part I can't figure out.
I can't figure out how exactly to dynamically populate the hidden input element with the value of the textbox. It's obvious that the underlying div giving the blue tag background is populated from the input element. But the input element is giving me a headache.
You see, I can't do the following:
-I can't simply "copy" the entire value of the current textarea and "paste" it into the input element's value, because that would override any previously tagged people in the input element (after all, the textarea can only possess plaintext).
-Even though I CAN locate the current index of the caret (the flashing black line in the textarea that tells you where you're going to be typing into), that's only for the textarea. Index position 10 in the textarea and in the input element's value might be different things, because this way of "tagging" people will result in adding additional characters to the value String.
-I can't simply do a "replace" of the text I am intending to replace, because there might be other instances of that same text in other parts of the value String.
I know it's a very long and confusing post, but I do hope you get what I mean. I really need a solution and I don't want to use contenteditable, because it's only for HTML5 and some older browsers might not support it.
Yours,
Rei
I hope you were able to come up with, or find, a solution to your problem. Since there doesn't seem to be one here, i'd like to offer one for and anyone who might stumble upon this (as well as you if my assumption was incorrect).
You are going to need to maintain explicit locational data of each existing mention in the textarea in the order in which they appear. If, after a modification of the content in textarea, the position of a mention in it is changed, you will need to determine which appearance of its value, if any, will be used to represent it, and appropriately update the locational data of the mention.
With such a collection of data, it becomes trivial to construct the value of mentionsHidden, though the existence of such data makes the element unnecessary.
Mentionator is an existing, robust solution which takes this approach in providing the functionality you are trying to recreate. Considering it is well-structured, easy to follow, and copiously commented, it should be of use to you as either out-of-the box solution or reference material to cite as you roll out your own. It is maintained by yours truly :) .

Ebook in iOS using CSS multi-column (calculate range of text in different column)

My first question here. Correct me if I've done anything wrong.
I've found a source here demonstrate how to paginate html using CSS multi-column.
http://groups.google.com/group/leaves-developers/browse_thread/thread/27e4bf5ff3c53113/f137dc01b6d853b7
My question is:
How to calculate the range / location of text in different column (page)?
For example, when changing the font size,
the text in current page will jump to another page.
To solve this, the program should save the current text location,
and move to the correct page (column) after reformatting the web page.
It is also useful for implementing bookmark function.
I think it should be done by javascript, but I'm new to javascript.
Any suggestions and tips are welcomed.
This is a bit of a general question, and very hard to answer, so I'll just try to point you in a direction I might try.
I have no idea how the layout of your page might look or function, but one way you could theoretically do this is by checking the text node of your 'column' whenever you change the font-size (presuming this font size change is implimented by a button click). So, for instance, say you have a div w/ the id #column_1, whenever someone clicks the button ui element you could evaluate the first several characters of #column_1, then search your string to find that text, and load whatever no. of characters you have defined as a 'page' around that text and call your render method to 'turn' to that page. So the flow of your function might look something like this:
zoomControl.click(click event){ //do something when you click the zoom control
var text = findTextofCurrentPage() //get the first bit of text of your current 'page'
renderLargerTextSize() //re-render your 'page' w/ larger text (for user feedback purposes)
renderPageWith(text) //render/navigate to the 'new' page wherein your the text in the variable 'text' can be found
}
Obviously this is a super generic 'idea' of what functions/methods you might use to make this happen, but I think if you dug into JS and say something like JQuery this sort of thing could be done relatively easily.

Categories

Resources