I can use document.createRange() to get a range of individual characters in a textnode, then Range.getBoundingClientRect() to get the position on the screen of those characters. I want to do the same thing for text in an input element, which used to be possible in IE only with createTextRange.
This jsfiddle sums up the problem - http://jsfiddle.net/sobu5ug2/2/
I can't use the same method for an input because Range.setStart/End only works on the element level, not the character level, for an input. I also tried window.getSelection().getRangeAt(0).getBoundingClientRect() but that returns a bounding rect of all 0's. I'd like to do this without actually selecting the text, if possible.
Is there any way to do this in a modern browser? Thanks.
Here is an example how to do it:
https://jh3y.medium.com/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
You need to create a div, copy all styles of the input element into that div, copy text up to the selection into that div, add a span inside that div with the selection and then you can measure this span relative to the div before removing the div again.
Related
I have a function that separates words into separate span tags and updates the div I am typing into but every time I update the innerHTML the cursor moves to the start of the box so the next character I type outputs behind the last one instead of after it. Could someone tell me how I can stop the cursor from doing this?
Here is my code
const editorDiv = document.getElementById('editor');
function wrapWords(str, tmpl) { //separates words into <span>
return str.replace(/\w+/g, tmpl || "<span>$&</span>");
}
editorDiv.addEventListener('keydown', function(event) {
editorDiv.innerHTML = wrapWords(editorDiv.innerText)
});
<div id="editor" contenteditable="true">
<span>hello</span>
</div>
When I type into the div text looks like this:
.siht ekil skool txeT
One way I have solved this in the past is to use 2 seperate elements, positioned on top of each other, with position: absolute, so that they overlap in a pixel-perfect manner.
The element on top is a regular input element, and its text color is set to transparent.
The element behind it contains the styled text (spans etc.)
This way, you can replace the styled content as the user types, without interfering with the input element's cursor at all.
Sorry that I don't have an 'essential code snippet' at hand, but I hope you get the idea. It did work well in practice for me.
I'm building an Editor that does syntax highlighting on code. My current approach is to just take the text in the editor (a div with contenteditable set to true) and check for regex matches in the string. I then replace the matches with <span> elements and apply some styles to those span elements. Then I replace the complete text in the divider with the new text using the .innerHTML attribute. This works just fine, but I have to type backwards, because upon inserting the text, my cursor is reset to position zero. I tried recording the value of selectionStart before inserting and then doing
element.selectionStart = oldSelectionStart + 1;
but it didn't work. I assume this is because of chrome's render pipeline, where JavaScript is run before rendering the page, and the cursor is reset upon render, not upon set... Can anyone help? How do I manage to keep the cursor where it was?
element.selectionStart works for input elements, not for contentEditable elements. You should try creating a Range object, set its startContainer & startOffset and collapse it to set caret at the required position. Refer to [https://developer.mozilla.org/en-US/docs/Web/API/Range] for details.
PS: If you would like to set your cursor to the 'end of line', you could do it easily with range.selectNode(requiredNode) and range.collapse(true) to move the caret to the end of node, i.e., line
I have a contenteditable div
<div contenteditable="true" class="editable"></div>
I append some html to it using :
$('.editable').html('<p><br></p>');
How I can set the caret before <br> ?
Setting cursor position in content editables is tricky business. The handling of cursor is browser dependent.
There are range and selection apis which are quite inconsistent over different browsers.
This is how to play with cursors:
Create a range object by document.createRange()
select the specified position by range methods (see the api)
Get selection object by selection=window.getSelection()
remove all existing selection by selection.removeAllRanges();
add the range to the selection by selection.addRange(range);
In this situations there is no text content in paragraph element, so you cannot place cursor inside it. You can either select the node by range.selectNode or you can place the cursor before/after the element.
Placing the cursor before or after will also behave weirdly in different browsers especially in edge cases (like in empty div or first or last postion).
You have to clearly specify what you want to achieve in the UI. Then I can suggest some logic to accomplish that.
I want to have a textarea with movable chip elements. However, I can't find anything that would allow me to move such an element e.g.
This (is) Sparta
to
This Sparta (is)
using drag&drop (see jsfiddle). How can I accomplish this?
Selenium Webdriver contains a function that returns only visible text inside element. I'd want to write a function that will get only hidden text inside element (i.e. all text that isn't visible in meaning of Selenium Webdriver W3C spec). According to to this spec element is visible only if all following conditions are met:
The element must have a height and width greater than 0px.
The element must not be visible if that element, or any of its ancestors, is hidden or has a CSS display property that is none.
The element must not be visible if there is a CSS3 Transform property that moves the element out of the viewport and can not be scrolled to.
OPTIONs and OPTGROUP elements are treated as special cases, they are considered shown if and only if the enclosing select element is visible.
MAP elements are shown if and only if the image it uses is visible. Areas within a map are shown if the enclosing MAP is visible.
Any INPUT elements of "type=hidden" are not visible
Any NOSCRIPT elements must not be visible if Javascript is enabled.
The element must not be visible if any ancestor in the element's transitive closure of offsetParents has a fixed size, and has the CSS style of "overflow:hidden", and the element's location is not within the fixed size of the parent.
Is it possible to write a JS function that will return only hidden text contained inside element? Do you know of any library that contains such function? How slow will such function be?
Yes, it is possible to write such code if you are just monitoring for display: none, visibility: hidden and no size or even an absolute/relative position that is off the screen. You would have to iterate every element in the page, determine if the element is visible and, if so, collect the text from any text nodes in that element.
It will be no slower or faster than any other function that iterates every node in the document. The total time will depend upon how efficiently the iteration code is written (it can skip all children of a hidden element for example) and on how long/complicated the document is.
If you want to be able to tell the difference between text that is outside the edges of an element with overflow:hidden or elements that might be marked for visibility, but be off-screen or out of view or out of the current viewable scroll area or pieces of text that might be obscured by other elements, that would be very difficult and honestly I don't know if all of that can be figured out from pure javascript.