Do Firefox and Chrome treat differently selections? - javascript

I‘m trying to get the caret on an element with contenteditable="true", with a handler. In Firefox all works as expected and if I put the handler for an onclick event it works fine on Chrome as well. The problem arises when the handler is for an onfocus event of the contenteditable, which is what I want to do.
This is the code I'm using only to try to figure out why is this happening.
var editor = document.querySelector("#editor [contenteditable]");
var blackboard = document.querySelector("textarea");
editor.addEventListener('focus', () => {
blackboard.value = getSelection().focusNode.data;
console.log(getSelection(), getSelection().focusNode);
})
<div id="maq">
<textarea rows="44" cols="50">
</textarea>
</div>
<div id="editor">
<div contenteditable="true">
<p>11111</p>
<p>2222</p>
<p>3333<br></p>
</div>
</div>
getSelection seems to return the expected object. If I inspect it in the browser console its focusNode is a nodeText in contenteditable; but if I try actually to use the focusNode it returns the object I was before going to the contenteditable. For example, if I click in some text in the navigation bar and then click in contenteditable, it returns the nodeText in the navigation bar.
Any idea why is this?
Thank you.

getSelection() doesn't return a DomNode but a Selection object which, based on your results, I suspect is a live value, just like how node.childNodes is a NodeList which gets automatically updated if you add or remove children.
So when the selection changes you magically get the new selection.

Related

How can I get HTML-Code in a contentEditable-DIV

In a contentEditable-DIV I try to get the HTML-Code from strat-Position 0 to end-position where the user has clicked.
<div id="MyEditableId" contentEditable="true">
1. Some text 123. <span style="background-color: #0CF;">text 123</span> 456 <span style="background-color: #9F3;">2-> abc </span>
<br />
<p> E.g. here is clicked: "click" Text after click </p>
<p></p>
<br />
end of text.
</div>
Something as below code snippet, which delivers the text from 0 to end of clicked node. But I need also the HTML-Code in contentEditable-DIV.
$('#MyEditableId').on('mouseup', function(event) {
var MyEditable = document.getElementById('MyEditableId');
MyEditable.focus();
range = document.createRange();
// endOffset: It will be better the length of where actually was clicked, e.g. after 15-characters. But this.length will be also ok.
endOffset = $(this).length;
range.setStart(MyEditable.firstChild,0);
range.setEnd(event.target,endOffset);
var selection = window.getSelection();
selection.addRange(range);
// Below I get the selected text from 0 to end of clicked node. But I need the selected HTML-Code from 0 to end of clicked position.
alert( window.getSelection() );
});
I expect for the result something as follows:
1. Some text 123. <span style="background-color: #0CF;">text 123</span> 456 <span style="background-color: #9F3;">2-> abc </span>
<br />
<p> E.g. here is clicked: "click"
How can I get the HTML-Code instead of text in my contentEditable-DIV?
Thanks In Advance.
You can select the div and use its property innerHTML
http://jsfiddle.net/at917rss/
<div id="MyEditableId" contentEditable="true">
1. Some text 123. <span style="background-color: #0CF;">text 123</span> 456 <span style="background-color: #9F3;">2-> abc </span>
<br />
<p> E.g. here is clicked: "click" Text after click </p>
<p></p>
<br />
end of text.
</div>
$('#MyEditableId').on('mouseup', function(event) {
var MyEditable = document.getElementById('MyEditableId');
MyEditable.focus();
range = document.createRange();
// endOffset: It will be better the length of where actually was clicked, e.g. after 15-characters. But this.length will be also ok.
endOffset = $(this).length;
range.setStart(MyEditable.firstChild,0);
range.setEnd(event.target,endOffset);
var selection = window.getSelection();
selection.addRange(range);
// get html for your div
var myDiv = document.getElementById('MyEditableId');
alert(myDiv.innerHTML);
});
Just change the alert line in your code to below one works well..
alert($.trim($('<div>').append(range.cloneContents()).html()));
I was just going through the documentation for Range and selection. You could use the extractContents() or cloneContents() method supported by the Range object like so in your case Demo:
var fragment = window.getSelection().getRangeAt(0).extractContents();
This automatically gets the first range that the user has selected. Although users can select multiple ranges by holding down the "Ctrl" Key. This gives the exact match till the cursor position for the simpler cases.
There are some caveats to this though. Both the methods extractContents() or cloneContents() return a documentFragment and the documentation clearly states that:
Event Listeners added using DOM Events are not copied during cloning. HTML attribute events are duplicated as they are for the DOM Core cloneNode method. HTML id attributes are also cloned, which can lead to an invalid document through cloning.
In essence, document fragments can contain invalid HTML and therefore you can not use all the normal DOM like .html() in some cases.
I found a relevant SO post on getting the cursor position on a contentEditable element and came across a TextRange Object (supported in IE < 9) and it has an htmlText property which returns the HTML source of the selection as a 'valid' HTML fragment. So in that case you would do something like:
var fragment = document.selection.createTextRange().htmlText;
However since most of the modern browsers support Window.getSelection(), it's good practice that you use it and build upon the suitable methods you have at your disposal. Hope it gets you started in the right direction.
Sidenote - Also from the docs:
using a selection object as the argument to window.alert will call the object's toString method

Insert data attribute in P tag in ContentEditable change event

I would like to insert a data attribute in the <p data-attribute="blah">...</p> tag when the user hits Enter (key 13) inside a content editable. I have gotten to the point of being notified when the user hits Enter but I am not sure how I can insert the attribute in the element the browser creates as default behaviour.
Any hints?
Thanks!
The best thing you can probably do is keeping track of the paragraphs. Once the contenteditable element has been created, you get a list of the paragraphs.
<div id="textArea" contenteditable></div>
Javascript (with jQuery, since you used its tag):
var textArea = $("#textArea"),
pars = textArea.find("p");
textArea.on("input", function() {
var curPars = $("p", this);
curPars.each(function() {
if ($.inArray(this, pars) === -1) // this is a new paragraph
this.setAttribute("data-attribute", someIndex);
});
pars = curPars;
});
Deleted paragraphs will be discarded - you decide what to do with them.
I used the input event because it's the most reliable event to keep track of changes on the content, including cutting and pasting from the clipboard using just the mouse. Too bad it's not available on IE8 and lower, and in IE9 it doesn't fire when deleting content (!), and IIRC not even on contenteditable elements. Uuuugh.
You may want to add the propertychange event too, or you can rely on just keypress and mouseup events.

Inline Ckeditor: All buttons are disabled

I am currently trying to add an inline ckeditor to some text.
No javascript errors occour but unfortunately all tools are disabled and I can't edit the text.
http://fiddle.jshell.net/5LuyD/
Any one have a clue as to what I am doing wrong?
What you're missing is contenteditable="true" attribute for your element. If you want to have the editor customized (i.e. ran via CKEDITOR.inline( element, cfg )), set CKEDITOR.disableAutoInline = true; first.
With CKEDITOR.disableAutoInline = true;, all the contenteditable="true" elements must be initialized manually to become an editor instance. See the official guide for inline instances.
You missed the contenteditable="true" attribute for the tags that are editable!
Here is the fiddle. http://fiddle.jshell.net/5LuyD/1/
For anyone having this issue despite setting contenteditable="true", there is an issue with Chrome where contenteditable is set to false if the element (or parent element) is not visible.
See: http://ckeditor.com/forums/CKEditor/Solved-Chrome-Toolbar-buttons-grayed-out-for-INLINE-editor
The solution is to either a) ensure the element is visible before calling CKEDITOR.inline() or b) use a textarea instead of a contenteditable element (CKE adds a contenteditable div after the textarea in this case).
I just had the same issue and I discovered a different fix for it. If the parent element (or the element itself) is originally set to display:none the contenteditable will = false (on chrome).
This fix worked for me:
var ck = CKEDITOR.inline(element);
ck.on('instanceReady', function(event) {
var editor = event.editor;
editor.setReadOnly(false);
});
Ref: https://dev.ckeditor.com/ticket/9814
I had the same problem and none of the other suggested solutions worked.
The problem was that the id attribute of the div started with a numeric character (it was a GUID). Changing the id to begin with an alpha character worked: all the editor buttons were enabled.
For some reason, ckEditor doesn't like id's that begin with numeric characters.
$(document).ready(function(){
for(var i in CKEDITOR.instances) {
var ck=CKEDITOR.instances[i];
ck.on( 'instanceReady', function( ev ) {
var editor = ev.editor;
editor.setReadOnly( false );
});
}});

Get caret position (character index) in input field on drop event (FF, Chrome)

I want an input field to accept only numbers, so, among other techniques, I also prevented the default drop event, if the result in the input filed is not a correct number. To achieve this, in IE I focus the field where I'm dropping the text, I search for the cursor position (character index), then construct the new value, from the old one and the dragged text.
To get caret position:
IE: Math.abs(document.selection.createRange().moveStart("character", -1000000)) Works
FF, Chrome: inputfield.selectionStart Does not work
Any ideas how can I get the character index in the drop handler?
There is a lot of discussion around how drag/drop support is implemented in browsers but the truth is they want to keep things protected. I mean theoretically you can call
event.dataTransfer.setData("Text", newValue)
inside your drop handler but it won't work due to "security reasons" they say. It's a pity since this would have been the best solution for you, I mean changing the text before it would have been dropped.
Instead I think you can overcome your problem with a small "hack"
<input id="target" type="text" value="aaa bbb ccc" />
<input id="source" type="text" value="good bad" />
and js
<script type="text/javascript">
$(function () {
var drop = false;
$("#target").bind("drop", function (e) {
// you can store the dragged text if you like
//var text = e.originalEvent.dataTransfer.getData("Text");
drop = true;
});
$("#target").bind("focus", function (e) {
if (drop) {
// you can get caret here, etc
$("#target").val($("#target").val().replace("bad", ""));
drop = false;
}
});
});
</script>
Tested in Chrome but it should work in all browsers too.
I hope this is what you were looking for.
Visit the following Link
https://groups.google.com/forum/?fromgroups=#!topic/javascript-information-visualization-toolkit/GJVG2laTaUo
I think it is usefull for you.

Backspace doesn't delete inner html tags of a contenteditable DIV in Firefox

I have created a DIV with attribute contenteditable=true and appended children like span and a with attributes contenteditable=false.
Wanted to test if the entire node be deleted with a single backspace and to my surprise Firefox couldn't delete the elements.
Also, this works as expected in all major desktop browsers except Firefox.
Any clues on this or what could be the possible workaround?
Found the exact issue on bugzilla here.
Okay! found the solution... its rather simple than what you would think. I am actually inserting html for links, so using <a> here. The <a> tag has attribute set to contenteditable=false and its not getting deleted with a backspace. So I have created an inner <span> level with contenteditable=true for firefox and that did the trick.
<div contentEditable="true">
<a href="your/url/path" contentEditable="false">
<span contentEditable="true">link here</span>
</a>
</div>
This is required in Firefox only. Other browsers treat this as expected with the span having content contenteditable=false.
I have faces the same terrible bug and had no choice but to make an elaborate javascript-based solution that listens to keypress events and if backspace was pressed, and the caret was just at the start offset of a text node, and the node before it is an element node, then delete that whole element node.
// credit: https://stackoverflow.com/a/25397485/104380
var isFF = !!navigator.userAgent.match(/firefox/i)
var editableElm = document.querySelector('div')
// listen to any key press
if( isFF )
editableElm.addEventListener('keydown', onKeydown)
function onKeydown(e){
if( e.key == "Backspace" ){
var selection = document.getSelection();
// if caret is at the begining of the text node (0), remove previous element
if( selection && selection.anchorOffset == 0 )
selection.anchorNode.previousSibling.parentNode.removeChild(selection.anchorNode.previousSibling)
}
}
<div contenteditable='true'>
Try deleting theme <mark contenteditable='false'>marked</mark> words on <mark contenteditable='false'>Firefox</mark> ok?
</div>
Try adding an "onkeydown" function on the editable div to stop event propagation (as follows). Worked for me:
onKeyDown = (e) => { e.stopPropagation(); }

Categories

Resources