When implementing a web-based rich-text editor, I read that document.execCommand is useful for performing operations on an HTML document (like making a selection bold). However, I need something a bit better. Specifically, I need to know exactly what text is added or removed from the innerHTML, and in what location (as an offset into the entire document's HTML representation).
I considered using the built in document.execCommand along side DOM4's mutation observer, but execCommand doesn't seem up to the task:
I don't see a way to "un-bold" a selection
the generated html seems to vary from browser to browser. (I'd like < span > tags not < b >, but consistency is more important)
and there's no information on what it does to handle redundantly nested/adjacent < span > tags.
Also, using mutation observer seems a bit overkill based on my needs.
My motivation: I'm trying to transmit document changes to the server periodically without re-transmitting the whole document. I'm sending the data as a collection of insertions and deletions upon the HTML representation. If someone knows a way to get this functionality out of, say, CKEditor (so I don't have to start from scratch), then I would love you forever.
Note: Performing a text diff is not an option, due to its poor performance on very large documents.
Otherwise, I'm not exactly afraid of trying to write something that does this. The methods provided by the DOM's range object would handle a lot of the heavy lifting. I'd also appreciate advice regarding this possibility.
There is one alternative to using execCommand - implementing the whole interaction of an editor including blinking of a cursor. And it has been done. Google does it in docs, but there's something free and open-source too. Cloud9 IDE http://c9.io has an implementation.
AFAIK, github uses that editor for some time now. And you surely can do anything under that, because there's no native code involved - like in execCommand
The repo is here: https://github.com/ajaxorg/cloud9 (it contains the whole IDE, you will need to find the code for the editor. )
Also - dom mutation events are deprecated. If you can drop support for old browsers, try mutation observer. If not - try to avoid detecting DOM changes at all and intercept changes in the editor's implementation. It might be the way to go for the new browsers too.
There is Trix rich text editor, from their description it looks like avoiding inconsistent execCommand is the whole point of the project.
It seems the new standard will be Input Events Level 2. To me it looks like it will be a revised improved version of execCommand.
Related
I am building a classic post related cms on LAMP stack, as a project to show at future interviews and land a job potentially. I want to build a rich text editor. After some research both in r/javascript and stack overflow I drew some conclusions.
Don't use "contentEditable=true" flag because according to one of
CKEditor's devs https://stackoverflow.com/a/11479435/10245890 is not optimal
The general consensus is to use iframe because of the isolation it
provides.
Yes I know I will not build the next CKEditor on my own, it takes
years of far more experienced people than me, I just want to learn
about the Javascript APIs.
Generally I want to use vanilla version of the languages in the
stack in order to get a better understanding of them.
The 'easy' way out to get a simple rich text editor going is to use execCommand but it is obselete. What I came up with goes like this:
function formatBold(){
var selection = document.getSelection().toString();
var originalString = document.getElementById("post-body-editor").innerHTML;
document.getElementById("post-body-editor").focus();
document.getElementById("post-body-editor").innerHTML = originalString.replace(selection, '<b>'+ selection +'</b>');
return;
}
document.addEventListener('keydown', function(event) {
if(event.ctrlKey && event.key === 'b'){
formatBold();
return;
}
return;
});
The function is called with a HTML button or key press. I saw in MDN that there is a method to implement insert Link, format a text with bold etc. Question is. I see some, if not many ,methods flagged as deprecated but they seem to be working. Should I use them or make it work on my own as shown above? I realize its not the most elegant solution but I believe it works fine for my level. Also any articles or other reading material is appreciated ofc.
EDIT:Formatting
If the question is just about using deprecated/obsolete features, the answer is: don't use. Though, I doubt execCommand would never really be removed from the browsers, that would break tons of existing pages ... If you really want to create a WYSIWYG editor, you've to dive deep in the world of DOM. In that world use of innerHTML is exceptional, you would work with Nodes and ShadowRoot etc.
Answered by Teemu in comment above.
On one of my sites, I used liberally to provide better hyphenation in the web browser. Unfortunately, they get corrupted by copying or cutting and pasting, so when people copy from my website, the ar-tic-les ap-pear with ex-tra hy-phens which are really annoying. I exaggerated it a bit here, but you get the idea.
I'd love a way to filter the selection on copy - basically an opportunity to remove the 's before they get to the clipboard. I suspect this isn't possible, based on what I've read/researched, but thought I'd ask the collective wisdom here, in case I've missed something.
A pseudocode example of what would be beautiful:
element.oncopy = function (ev) {
ev.selection.replace(//g, '');
return true; // or ev, I suppose
}
Have a look at this article about the oncopy event. I think it is exactly what you need: http://help.dottoro.com/ljwexqxl.php.
Example #2 on the following page explains how to use the clipboard in a cross-browser friendly way (since only IE has access to the clipboardData object used in the first article): http://help.dottoro.com/ljxundda.php
That page also mentions that there are some cases where security restrictions may prevent the cross-browser method from working, which is why some sites use Flash to manipulate the clipboard. Here's an article which discusses that method, in case it sounds like what you want: http://www.jeffothy.com/weblog/clipboard-copy/
EDIT
Have a look at Hyphenator.js. It is a JavaScript method of hyphenating text intelligently on the client side. Quickly playing around with the demo (which can be found here), it appears to leave the hyphens out of copied text. It might be a pain to change your content to use this instead of , but it looks like it will achieve all of your goals.
I need to make the user to be able to select some text, click a button and make the server remember the selection for the next time.
I've extensively read through SO's questions and answers, tried some libraries, but without luck: haven't found a reliable tool yet.
It isn't important how the selection's boundaries are identified: it could be "nth textNode, mth char", or "nth char of text", or "nth char of html", or whatever, as long as it allows the server to identify the points in the document; what really matter is that, selecting the same words of the same document must give the same result on chrome, safari, IE, firefox.
EDIT: I don't need it to work everywhere on the internet: just on one site, where the document's structure is fixed and only the content of a single div (or the like) will change.
Try my Rangy library and its Serializer module. I'm not convinced it's exactly what you want because you mentioned the server remembering the selection, whereas my suggestion uses cookies, and the serialized selection will vary between browsers. However, it does do as you described in the first paragraph.
On the other hand, it's pretty much impossible to write something that will work for all browsers and all pages, since browsers interpret HTML differently and build different DOMs.
I need to make a comment mechanism in which user highlights a piece of text, clicks "comment this", and then does something. The Javascript code has to know not only the selected text (this is trivial), but also the anchorOffset, to know exactly from which to which character the text was selected.
I've found a cross-browser solution that gives you the text. Is there a reliable way to get a selection object as it's described in DOM specs?
You could try IERange, which creates a selection-like object in IE and adds a getSelection() method to window. I don't think it's perfect but it's about the best standalone Range/selection library there is that I've seen.
Update
I've been working on a Range/selection library called Rangy that goes beyond what IERange provides. There's an early release available at http://code.google.com/p/rangy.
I've been looking into javascript test suites and I have found QUnit to be very interesting. I understand how to test computational code, but...
How do you test javascript applications written primarily for DOM manipulation?
it seems like testing the position/color/etc of DOM elements would be a moot point because you'd end up doing somethign like this:
$("li.my_element").css("background-color", "#f00");
and then in your test...
$(function() {
module("coloring");
test("test_my_element", function() {
var li_element_color = $("li.my_element").css('background-color');
equals(li_element_color, "#f00");
});
});
this just doesn't feel right because it basically just doing this:
var my_li= $("li.my_element");
my_li.css("background-color", "#f00");
if ( my_li.css("background-color") == "#f00" ) {
return true;
}
Am I nuts? How is this supposed to be done?
edit: the heart of the question:
I guess what I'm getting at is, I need to make sure the code isn't broken before I deploy, but the vast majority of it is UI helpers and ajax. How do I test that things are appearing correctly?
A few examples:
test that a JQuery UI dialog is appearing on top of all other elements
test that the drag-n-drop is working properly
test that the color of a droppable changes when an element is dropped on it
test that the ajax is all working properly
test that there are no extraneous commas that will break IE
I have found the Javascript/DOM tests, especially for the simple interactions that you are describing, are not that useful. You'll testing that things are set up right, and since jQuery is so declarative, your tests look a lot like your code.
My current thinking is that if you are writing larger JS components, it makes sense to pull out a set of interrelated behaviors both into a jQuery plugin and a set of tests for it.
But from the examples you mentioned, it sounds like you're really looking for a level of protection within your integrated website. A tool like Selenium will probably be more powerful and appropriate for you. In particular, it
can be automated
can run against multiple browsers, including IE
runs within the context of your web app and pages, so drag-n-drop can be tested where it really happens instead of in some test environment.
AJAX can be tested
Instead of testing the JQuery css function. Your test should mock the css function, and ensure that it is called only once with the correct color. The code tested should be yours, not the frameworks.
In addition to what Jason Harwig is saying, I would say that unit testing is a test to make sure that code is being run as expected. If you want to test that, then Jason is absolutely right about how you should do that. If you are wanting to run tests to check that the DOM manipulation is happening (UI testing) and not the actual code that is doing the DOM manipulation (unit testing), then you may want to check out something like Selenium, WatiN or Watir.
I'm guessing that many people test visually: i.e. they look at their browser's output on their monitor, to see whether it looks like the DOM was manipulated as expected.
If that needs to be an automated test case (eg. for regression testing), then maybe they record the output (like screen capture) and do something like compare two screenshots to see whether the results are the same.
Instead of capturing a screenshot, you could just capture the whole DOM, and do a side-by-side comparison of the captured DOM trees (which might be less error-prone that comparing pixels).
I test AJAX stuff like this:
Make the AJAX call
Set up a JavaScript timer
Check the DOM to see if the expected changes have happened
Now, it could be that the AJAX call hasn't returned before you do your check, but this is also useful test information; with an AJAX call, there is (usually) some time after which we'd call it a failure. As an example, if we're doing a suggestion popup, and it's taken 30 seconds to come back, that's a fail.