I am working on an application that keeps track of edits in a contenteditable section of an HTML page. When something is inserted, this content is wrapped inside a span with class inserted. So far, so good. But I need to keep track of where the text cursor is inside the contenteditable area (which might have multiple spans inside).
Here is a small section of html that I am using to test my code (only the relevant portion of the page is shown here):
<p id="p1">
<span id="p1s1">This manual is about</span>
<span id="p1s2" class="inserted"> product X</span>
<span id="p1s3" class="deleted"> something</span><span id="p1s4">.</span>
</p>
When I click right after the word 'product' the result is as expected: the anchorNode is identical to the focusNode and shows the ID 'p1s2' with an offset 8. Clicking after the p of product shows offset 2. But clicking in front of the 'p' gives me an offset of 1 but an ID 'p1' - which is the parent of the node in which I clicked.
I tested this with the latest versions of Chrome and Safari and both browsers show the same result. Is this intended behaviour and why do I not get the ID of the span if I clearly click inside its content ?
Related
When I have configured some block formats/styles which I then select from the Format dropdown, how can I remove/unselect/unapply them from my content so that the formatting is no longer applied to the given paragraph? Eg. I have <h5 class="something"> that I need to have removed from the paragraph in the content.
I have tried to make another entry in the dropdown which is defined as just [title: 'Regular'] (without any block, inline tags etc.) but selecting that doesn't do anything.
I also tried the Clear formatting button, but to my surprise that only removes the class, not the <h5> tag itself!
PS. I not talking about removing them from the dropdown menu, but how to remove them from the content in the editor.
Oh my goodness, is it really that simple. When you look at the dropdown you see a thick gray line on the left side for those formats that are applied to the current paragraph. And then you just choose the same format in the dropdown again - and it will be removed!
I was trying to achieve something like Quora's read on feature where when expanded more content will be displayed.
I am using ng-bind-html binding an html page into P element.
<p ng-show="viewModel.level==0" ng-bind-html="viewModel.content0 | sanitize">
<p ng-show="viewModel.level==1" ng-bind-html="viewModel.content1 | sanitize">
the expanding works fine, but when collapse from level 1 to 0 (more content to less), the html page went into blank stuck there until you click or drag the page.
how to avoid that ?
I get the answer by accident.
what I need is a notification that the list size has changed:
$scope.$broadcast('scroll.resize');
I'm trying to create a textarea that listens to URLs typed, pasted or dropped and converts them to links. These links are not editable and must be treated as blocks. Delete and backspace must delete them. Finally, it would be nice if they show full selection when the caret is moved through them.
Problems: after spending too much time and trying some of the suggested solutions found here on stackoverflow (Tim Down), I still couldn't get it to work correctly. I'm finding it very hard to have the caret in the correct place all the time and after pasting a link and the automatic conversion is done, the selection is locked and no input can be done. I tried to insert and keep a zero width character before and after each link but then other selection problems started to occurr.
Can someone please have a look at the following fiddle which represents a simplified version focusing the problem and point me to the right direction for the correct keyboard navigation through these non-editable links inside a contenteditable div?
jsFiddle here
$("#customtextarea").on("input propertychange drop paste", function (e) {
var $this = $(this), savedSelection = saveSelection($this.get(0));
var parsedHtml = getParsedHtml($this.html()); // This gets the innerhtml with the urls turned to links
$this.html(parsedHtml).focus();
restoreSelection($this.get(0), savedSelection);
});
EDIT: I've taken the approach suggested by XuoriG and I'm still facing more or less the same issues as before: caret gets 'stuck' after the link and the links can't be deleted. Also keyboard navigation (left, right) is not working.
New jsFiddle here
When using $this.html you're replacing the entire DOM and losing your selection.
The way I was able to do this is by creating and removing DOM nodes on the fly on every key up. I can't post code for you right now but basically you go through every node on key up and create a new anchor tag with the content matched by your url regex and remove that from text node. Repeat until you dont have any more matches in the node. You'll be able to save and restore the selection this way.
The hard part after is being able to append to an existing url. The way I did it is by detecting if a text node is right next to an anchor tag, and if the anchor text + the text node matches an url too you append the text node to the anchor tag and remove the text node.
Apologies for the long question, but I've tried a lot of things and done some research and havent found much of a solution. I have a content editable div.
<div contentEditable=true onkeyup='showResult(this.lastChild.textContent)'></div>
When a user types something into this div, the showResult javascript runs which is basically an ajax request which returns a list of items that match. When a user clicks on one of the suggestions, say the name "John", a span with the suggestion is added into this contentEditable div like so:
<div contentEditable=true onkeyup='showResult(this.lastChild.textContent)'>
<span id='uniqueId1' class='SpanClass' contentEditable='false'>John</span>
</div>
Having selected one Name, the user may want to search for another name. It HTML terms, that means that they would be typing the following:
<div contentEditable=true onkeyup='showResult(this.lastChild.textContent)'>
<span id='uniqueId1' class='SpanClass' contentEditable='false'>John</span>
New User Text Goes Here
</div>
On Chrome, the right behaviour happens when the user continues tries typing in the div - the showResult function runs on the new text that the user types in and ignores the span elements. For Example, if the user types in "Fr" having already selected John, it ignores the first children (John), and sends what the user typed off via ajax and returns suggestions like Fred and Frankie.
However, in IE the span is still content editable and the user can't add any text other than within the span, which seems to make no sense as it is clearly contentEditable=false The ajax request is therefore run on the "John" text plus whatever the user types in next, which is not what I'm trying to achieve.
Finally, in Firefox, the span is not contentEditable BUT the lastChild bit only picks up text within the span, and ignores the text the user puts in.
I've console logged the results of showResult(this.lastChild.textContent) to see what is being sent to the ajax request.
In Chrome, typing in "Fr" in the box after the "John" span sends "Fr" to the ajax and returns the right result.
In IE, typing in "Fr" in the box sends "JohnFr"
In Firefox, typing in "Fr" just sends "John".
As the issue is with this lastChild and the span, I've also included the Javascript that creates the span element. This only activates after a successful result is return and the result is clicked on. (please excuse the very messy Javascript/Jquery)
$('body').on("click", '.TagHints', function(){
//Once you click on the suggestion
var ThisData = $(this).data("id");
var ThisId = $(this).attr("id");
var ThisTag = $(this).data("tag");
//delete the text that the user typed in
elementToRemove = document.getElementById("FakeInput").lastChild;
document.getElementById("FakeInput").removeChild(elementToRemove);
var TagDiv = document.createElement('span');
TagDiv.className = 'SpanClass';
TagDiv.id = ThisId;
TagDiv.innerHTML = ThisTag;
TagDiv.contentEditable=false;
//append the Span to the contentEditable div
document.getElementById("FakeInput").appendChild(TagDiv);
var TagHints = document.getElementsByClassName("TagHints");
while(TagHints.length > 0){
TagHints[0].parentNode.removeChild(TagHints[0]);
}
});
Why are the three browsers behaving completely differently and how do I get them all to behave like Chrome is? Is there a better way of getting the text not in the spans?
I read on another answer that firefox likes inputs and IE likes breaks in this context but both do not seem to work for me. :-(.
One big stopper to good solutions is that jQuery stops working after about line 6, which has also completely baffled me. If anyone can explain why its not working, that would also be cool. Maybe something to do with it being an ajax query and content being created after jquery is loaded?
Thanks for your help!
So, although I haven't had any responses I've come up with a workaround. However, lastChild is still behaving differently between the three browsers so would appreciate any further input.
The answer was to have a function in the document ready section which cloned the original div, stripped it of its children and grabbed the text on key up using Jquery. This then passed the result to the showResult function like so:
$('#FakeInput').keyup(function(){
var ThisClone = $('#FakeInput').clone()
.children()
.remove()
.end()
.text();
showResult(ThisClone);
});
Equally, rather than trying to remove the text that the user typed in from the divs when the user makes a selection I simply cloned the items in the span, using their class, then emptied the contents of the div and reappended these cloned divs.
var TagItems = $('.TagItems').clone();
$('#FakeInput').empty();
$('#FakeInput').append(TagItems);
This solution works across the three browsers.
I have a big page, with full of (server side) generated information organized into "chapters". To allow an easier overview for the user I put a little element with CSS fixed position to the top right corner of the page.
<div class="selector">Goto section within the table: <select
id="chapterselector"
onChange="goto_section('chapterselector')">%SELECTOR%</select>
</div>
The text "%SELECTOR%" is replaced by the server side component to the correct option elements.
function goto_section ( element ) {
element = document.getElementById(element);
window.location = '#Chapter_' + element.value;
}
This is the JavaScript part for now. This works nicely. However one little issue remains:
Users (including me) can use the select menu to jump inside the document, but then often cursor arrow keys would be used to navigate to scroll the page. The problem: after using the select menu, it has the focus, so cursor keys now "scrolls" the possible choices inside the select menu. What I want: after using the select menu, I want it to lose the focus automatically, so cursor keys scrolls the page.
How can I do this? Thanks for any suggestion.
element.blur()
try that after setting the location