I have a contenteditable div in my GWT application and when I press backspace or delete key, I want to get the node before and after caret position and check whether it is a text node or not.
Element element = DOM.createDiv();
element.setAttribute(contenteditable, "true");
basePanel.getElement().appendChild(element);
This is how I created the content editable div.
Any solutions will be appreciative.
Regards.
Dig into selection and range classes. They are not yet browser compatible, so you might use
https://code.google.com/p/rangy/ or
jquery++
for abstraction. You then create a range for your div, expand it by 1 on both ends. then you examine startcontainer and endcontainer to find out their node type.
The selection and range apis are not overly beautiful and working with them is more involved than necessary, but that is the way to get it done.
Related
I would like a callback function to be executed each time the caret or selection moves in the text area.
Anybody knows how to do that?
Edit: I know how to retrieve the carret position. I'm interested in how to watch for the change. With my current knowledge i would test carret position on each keyup / mousemouve / mouseup. Is there a smarter (and more performant) way to do it. Are there event that may change the carret position that i did not think about ?
There's a DOM event onSelect that you can use, but it will only pick up caret movements when that movement affects the selection. Here's a JSBin with a demo:
http://jsbin.com/eyaril/4/edit
Just select text in the box and view the output in firebug.
In the end I finally found after exploring each of possible solutions that there is no performant way to do that for large amounts of text.
I have no other solution than waiting for my web application to meet the technical requirements to use a modern text area replacement library (= not depending of ie quirks mode).
This one's a challenge:
Suppose you have two text inputs, as in this fiddle. When the user "scrolls" inside of one (e.g. by moving the cursor to the far right or left), I'd like to "scroll" the other so that it stays in sync. Can this be done, at least in modern browsers?
I would change the text that isn't being input to an IFrame. If both need to be inputs, then dynamically switch your element from iframe to input when it is focused on/away from.
In the input box, you can find where the caret is by using the selection properties; then the IFrame can then be scrolled using scrollTo.
I'm afraid I don't have a complete solution for finding out exactly the scroll state of the input box.
I know getting / setting cursor position in a contentEditable is damn near impossible. I don't care about knowing this information. I need to be able to save current selection, modify innerHTML of the div, and then restore the selection.
I've bee experimenting with the answer provided at contenteditable selected text save and restore . It works after typing in the div, but not after programmatically modifying the innerHTML of the div. Instead, when I call restoreSelection, the caret simply goes to the beginning.
Any suggestions as to how to be able to save / restore selection on a contentEditable after modifying the innerHTML instead of typing would be much appreciated.
If you're doing some kind of string substitution on the existing innerHTML of your editable element, you may be able to use my Rangy library and its save/restore selection module. It uses invisible elements with particular IDs to mark the start and end boundaries of the selection, so if your innerHTML change does not include these elements then this will not work.
Another alternative is to do it based purely on character indices within the text nodes of the element. I've written a naive implementation here: https://stackoverflow.com/q/5596688/96100
Is it possible to get the current viewport Range (the visible part of the page inside the browser) using XUL functions from Javascript or plain Javascript?
Thank you!
For the viewport you need to use document.documentElement.scrollTop/scrollLeft/scrollHeight/scrollWidth. There is a slight complication: I think that in quirks mode (document.compatMode is "BackCompat") you need to check these properties on document.body instead of document.documentElement.
See https://developer.mozilla.org/en/DOM/element.scrollTop for documentation.
Edit: It seems that you aren't really interested in the viewport but rather its contents. AFAIK there is no generic way to get the contents of a particular area of the web page. It definitely cannot be described by a single Range object, rather a set of ranges. And even then: if the element has lots of text and all of it is a single TextNode, you won't know which parts of the text are visible and which are not.
However, in some special cases (particularly when the page structure is simple) you might be able to learn what text is being displayed by using range.getBoundingClientRect(). You start by selecting everything in your range and reducing that selection until the range size is within viewport boundaries.
Here is an example that does it for a vertically scrollable <div> containing lots of text: http://jsfiddle.net/5vEdP/ (tested in Firefox 6, Chrome 14 and IE 9). It first needs to make sure that each text character is placed into its own TextNode, otherwise you won't be able to select it separately in a Range object. It then selects the container of the text and moves the start of the range until the top boundary of the range is below the top boundary of the container. And then it does the same thing for the bottom boundary by moving the end of the range. In the end you get a range that selected only the text nodes that are fully visible.
width :document.body.offsetWidth;
height :document.body.offsetHeight;
HERE are better examples for various browsers
Let's say I have a set of contenteditable="true" divs.
<div id="0" contenteditable="true"></div>
<div id="1" contenteditable..></div>
<div...etc></div>
I can't have one div, multiple divs is a must. How could I highlight the content of more than one div? Using ranges? Anything else?
The answer is that it depends on the browser. See this example for a test of two methods using Ranges. The first attempts to create a Range per editable <div> and add all of them to the selection. The second attempts to create a single Range encompassing the contents of two editable <div>s.
Results:
In all browsers, it is impossible for the user to create a selection that lives in more than one editable element;
Firefox is the most permissive of the major browsers. Both programmatic methods work.
Safari and Chrome is the least permissive: neither method selects content from more than one editable element.
Opera 11 does not support multiple Ranges in the selection, but does support a selected Range spanning more than one editable element.
IE pre-version 9 does not support DOM Range or the same Selection API as other browsers, but the equivalent TextRange code does not allow selection from more than one editable element.
It is possible to switch the divs to contenteditable="false" on the fly as a consequence of starting a selection in one of them. Something like this gives you the idea (using JQuery):
$('div').bind("selectstart", function() {
$('div').attr("contenteditable", false);
});
Here's a fiddle to demonstrate (only tested in Chrome).
Note in the fiddle example that the first ContentEditable Div gets the focus. This allows you to type away as normal, but as soon as you select anything, using mouse or keyboard, you'll see you can extend the selection across divs as usual.
This obviously needs fleshing out to work with multiple browsers and to turn back to contenteditable="true" appropriately. But I think the approach is valid.