Pell – A simple and small rich-text editor for the web
github.com
github.com
That, plus a few crude buttons, is all this does. As several other comments point out, there's good reasons why real WYSIWYG packages are bigger—the user experience of working with a plain contentEditable element is still terrible, the output HTML still a complete mess. If you don't care much about that, you don't need an editor component, since setting an attribute and wiring up some buttons to call `execCommand` is easy enough to do from scratch.
Disclaimer: I work on one of those 'bloated' editors, http://prosemirror.net , and find it a little annoying when someone implies their crude afternoon hack is somehow equivalent and we're crazy for putting in all that effort.
There's a solution in the works for the latter problem.
P.S. Just because something is small, doesn't mean it's a crude afternoon hack :)
While I'm certain you have good intentions, the size comparison chart at the top of the README is a bit misleading. It would be fair to list out the trade-offs on going ahead with ContentEditable so that users can know what they are getting if they choose Pell.
To clarify, Pell looks awesome and I don't intend to diss on your project or your hard work, just that the way the project's README is laid out I feel @marijn's response is not totally unwarranted.
Unfortunately there's no way around this short of not using contenteditable - which is even worse sometimes.
Most editors feature customized builds which let you reduce the footprint of the editor. If you're looking for quick gains first and foremost disable support for pasting from other applications, e.g. MS Word. It's always a major feature.
For instance, react-native, electron or windows store apps.
But if you decide to freeze you run the risk of exposing your users to security issues.
You only need to check periodically whether new updates are ok. Same way you don't freeze all your non-editor dependencies for eternity.
And I doubt you need to care for layers below the browser ("OS and so on") changing when it comes to your WYSYWIG component in your Electron app.
Anyway we had our share of trouble with a customer due to an exotic rendering issue that occurred in two consecutive versions of Chrome and then disappeared, so yeah.
I'd say it's the exception and not the rule, though.
I think it's the opposite: if you're using react-native or electron you might as well give up on optimizing the size of your own code, because it's not going to make a dent in your overall level of bloat. Enabling your codebase to be used from the user's own browser would have a much better return in bloat-reduction, even if it meant writing more of your own code.
> And in the case of windows store apps, the runtime is already on the users systems and they're just downloading the packaged JS/HTML
Sure, but making your site windows-store-only is morally the same thing as making it single-browser (possibly even single-version-of-that-browser). The flip side of "all that "bloat" is there so that you'll get consistent results across browsers" is that you can cut a lot of bloat if you're willing to sacrifice crossplatform support.
Not every app can be ported to the browser. For instance, if I wanted to make a local file manager (like fman) I couldn't do that in the browser. This is why native apps exist.
if you're using electron in the first place, I think its safe to assume that re-inventing electron is not within your skillset.
You raise a good point though. If they could make electron more modular it would save on a lot of that bloat everyone always complains about. I bet half the apps could make do without audio, image processing etc
That said (and not to take thunder away from your page), this is possible because the HTML spec itself allows for any element to be made into a "WYSIWYG" editor by adding the "contenteditable" attribute.
What most other larger editors are doing is working around some of the horrendous non-standardized versions of execCommand which varies across browsers.
Ideally a web standards body should come out with a <input type="richtext"> element, which is controlled through a sane API, and the spec spelled out to support everything by default. And get rid of the bane that is rich text editing on the web.
I note that `contenteditable` itself began as a proprietary DOM/HTML extension by Microsoft in IE5.5 back in 2000 - Gecko didn't support it until 2003. In the 2008 draft of the HTML5 specification[1] the `contenteditable` attribute's summary seems to imply that it's only featured because it "is a common attribute".
[1]: https://www.w3.org/TR/2008/WD-html5-20080610/editing.html#co...
There are web extensions that let you do that but it's often a bit clumsy I found, so I just tend to edit my text in Emacs and then copy/paste it in the box.
Regular <textarea> editing on the web is not generally a pleasant experience (I just had to grab my mouse to extend the comment area so that I can actually see what I'm typing) but rich text "WISIWIG" editors are even worse because the edition shortcuts always intersect somewhat with the regular browser shortcuts (sometimes in fun and interesting ways), they're slow and generally simply not up to par with stand alone editors.
Why does everything need to be in the browser? It's a complete anti-pattern as far as I'm concerned. Emacs is often mocked as the "emacs OS" because of how extensible it is but generally this is done by calling external programs, not reimplementing everything in elisp from scratch.
The tricky part is the user interface, text transformation (sed, query-and-replace, pipe through external programs...), text parsing (for syntax highlighting and code folding for instance) and invoking external applications, for instance to compile code or post-process your document. Web browsers are not particularly good at any of these things since they're not needed to purely "browse" the web.
If you want performance while handling big files you also need to optimize the way you stream data from the disk, something browsers are terrible at. Is there any web-technology-based editor that handles big files decently? Last time I've seen a benchmark it was pretty abysmal. Not that Emacs is great at it (it was pretty bad for a long time actually, especially pre-64bit computers) but it still was orders of magnitude faster than Atom for instance: https://github.com/jhallen/joes-sandbox/tree/master/editor-p...
Not a bad idea, but for a lot of users they'd want that external editor to be MS Word rather than emacs and I suspect that would cause a lot of other issues....
IF this ever came about (big if) it should support native conversion between markdown / html and other formats. That stuff is a nightmare
Consider <input type="imagepickerwithfilters"> or <input type="spreadsheet">. Useful sometimes, but implies too much standardization.
I'd like to mention a few things about pell.js and it's goals:
- It was not made to be the most full-featured editor out there
- It was not made to fight with or worry about browser inconsistencies (browsers are converging, quickly too)
- It was made to demonstrate the underlying simplicity of something that looks like magic to most beginner developers (including myself) and to teach people how to use `contentEditable` and `execCommand` via a tiny, extremely readable codebase
- It was made for people who need just a basic WYSIWYG editor in their app/site/whatever and who care about bundle size (PWA's anyone?)
Over time, many of the inconsistencies and issues will get fixed or implemented in pell, but the goal of remaining tiny yet functional will always remain paramount.
I think it's good to showcase new ways of solving old problems using the latest in browser tech.
PS: Also working on one of those bloaty editors.
So again, don't suggest we (editor implementors) are clueless or behind the times. We happen to have spent quite a bit of time on this stuff. We know it.
By "fallen behind" I meant still making the assumption that many users are still using outdated browsers. This is not the case anymore, especially in the last year. And because of this fact, many WYSIWYG editors might be implementing a lot of cross-platform bloat that just isn't necessary any more. Rather than raging, perhaps browse through the ProseMirror source and see what you can remove as "obsolete" code.
Try quoting the last paragraph in the editor. It's now impossible to ever escape from the quote. All new text at the bottom stays "quoted".
From a cursory test of the editor, it suffers from basically all the issues that contenteditable suffers from.
The reason other editors are so big is because the take editing behaviour into account.
That's the same problem.
At the moment caret positions in browsers are limited only by char-positions. There are no carets in between box blocks.
Yet consider my question on SO : https://ux.stackexchange.com/questions/72309/caret-positioni...
It doesn't take it into account as much as it hoists an implicit imperative programming language onto it.
Of course learning markdown for a non-technical user isn't easy, and having a separate "Preview" pane to show the final product certainly isn't a great UX. But it is a delight compared to "for a simple URL, I type the URL, click <Enter>, then either click the 'X' on the big preview graphic that inescapably appears or click <ctrl-z> then enter again to get rid of whatever <div> monstrosity Yahoo just added to my message."
I feel like this is a leftover of the old times, back when first WYSIWYG editors appeared and these capabilities were impressive. I think these days what one needs is not bold/italic, but facilities for editing structure, inserting links, and positioning images, all according to a style template.
Those of us who do care about semantic structure can often do without WYSISYG editors anyway, since we have much cooler alternatives such as Markdown and LaTeX.
In every day usage, bold/underline/strike may be superfluous, but I would argue that italics are essential. Without at least the ability to emphasise a word in some way, your only option is uppercase.
Trix sidesteps these inconsistencies by
treating contenteditable as an I/O devi-
ice: when input makes its way to the ed-
itor, Trix converts that input into an
editing operation on its internal docum-
ent model, then re-renders that document
back into the editor. This gives Trix
complete control over what happens after
every keystroke, and avoids the need to
use execCommand at all.
Trix is from basecamp, and has a super healthy plugin/extension ecosystem - https://github.com/basecamp/trixFull disclosure: not affiliated at all, but the rare times I need a WYSIWYG editor I tend to look for Trix and be very pleased.
> When Pepsi-pusher John Sculley was developing the Apple Newton, he didn’t know something that every computer science major in the country knows: handwriting recognition is not possible. This was at the same time that Bill Gates was hauling programmers into meetings begging them to create a single rich text edit control that could be reused in all their products. Put Jim Manzi (the suit who let the MBAs take over Lotus) in that meeting and he would be staring blankly. “What’s a rich text edit control?” It never would have occurred to him to take technological leadership because he didn’t grok the technology; in fact, the very use of the word grok in that sentence would probably throw him off.
My point: Microsoft has identified and satisfied this need 20 years ago. Although I'm a big supporter of the Open Web and open systems in general, sometimes I do long for a bit of dictatorship to get things done. But then I come to my senses :p
If it has an end user that is not yourself, I can guarantee you they do care - even if it's not a conscious realisation.
If it's 10 internal admin users on desktops, the only time they're going to have to redownload the bundled js is when there's a change.
And if you're sensible you'll have multiple bundles, one of which will be a bundle of rarely updated external libraries, including that editor. So once every couple of months they have to wait an extra second or two for a page to load.
Some of the internal apps I've written for clients have a core bundle that almost never changes and then a js file almost per page (which each append a hash of their contents to the URL to ensure efficient cache busting, managed with an in memory cache of the hashes).
Great project.
That is if you typed:
**blah**
It would show blah and still show the asterisk but in bold (apologies for using code format but HN will strip the asterisks).There are of course some editor plugins, and IDEs that do this but I haven't seen one on the web.
EDIT Oh apparently there is stackedit (I hadn't googled in awhile).
Line one
<div>Line two</div>Firefox gives me
Line one
<br>
Line two
<br> !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.pell={})}(this,function(e){"use strict";var t,n=Object,i=document,o=prompt,r="pell-",a="button",u="className",l="appendChild",d="createElement",c="insert",s="rderedList",f="Horizontal",p="formatBlock",m="Enter the ",b=n.assign||function(e,i,o,r){for(i=arguments,t=1;t<i.length;t++)for(r in o=i[t])n.prototype.hasOwnProperty.call(o,r)&&(e[r]=o[r]);return e},g=function(e,t,n){return{icon:e,title:t,result:n}},h=function(e,t){return i.execCommand.bind(i,e,!1,t)},k=function(e){return/^https?:\/\//.test(e)?e:"http://"+e},L={bold:g("<b>B</b>","Bold",h("bold")),italic:g("<i>I</i>","Italic",h("italic")),underline:g("<u>U</u>","Underline",h("underline")),strikethrough:g("<s>S</s>","Strike-through",h("strikeThrough")),heading1:g("<b>H<sub>1</sub></b>","Heading 1",h(p,"<H1>")),heading2:g("<b>H<sub>2</sub></b>","Heading 2",h(p,"<H2>")),paragraph:g("¶","Paragraph",h(p,"<P>")),quote:g("“ ”","Quote",h(p,"<BLOCKQUOTE>")),olist:g("#","Ordered List",h(c+"O"+s)),ulist:g("•","Unordered List",h(c+"Uno"+s)),code:g("</>","Code",h(p,"<PRE>")),line:g("―",f+" Line",h(c+f+"Rule","<PRE>")),link:g("","Link",function(){(t=o(m+"link URL"))&&h("createLink",k(t))}),image:g("","Image",function(){(t=o(m+"image URL"))&&h(c+"Image",k(t))}),undo:g("↺","Undo",h("undo")),redo:g("↻","Redo",h("redo"))};e["default"]={init:e.init=function(e){var o=e.classes,c=e.actions,s=i.getElementById(e.root),f=i[d]("div"),p=i[d]("div");p.contentEditable=!0,f[u]=o.actionbar||r+"actionbar",p[u]=o.editor||r+"editor",p.oninput=function(t){return e.onChange&&e.onChange(t.target.innerHTML)},s[l](f),s[l](p),(c?c.map(function(e){return"string"==typeof e?L[e]:b({},L[e.name],e)}):n.keys(L).map(function(e){return L[e]})).forEach(function(e){t=i[d](a),t[u]=o.button||r+a,t.innerHTML=e.icon,t.title=e.title,t.onclick=e.result,f[l](t)})}},n.defineProperty(e,"__esModule",{value:!0})})
However, when you consider gzipping, this represents savings of roughly 12–16 bytes (it depends a little on whether the files are noeol and whether you avoid file metadata going in the gzip stream—pro tip, make your gzipped files smaller with `gzip < x > x.gz` instead of `gzip x`). Quite a few of the latter “save another one or two bytes” tricks that I employed not only increase runtime trivially (e.g. concatenating two string literals instead of using one string literal), but they also increased gzip size by four or more bytes. (The tricks for the strings “Enter the ”, “Horizontal”, “rderedList” and “insert” had saved seven bytes ungzipped at the cost of about 21 bytes gzipped, and “pell-” and “button” had saved one byte at the cost of eleven gzipped.) The lesson there is—gzip is pretty good, and deduplication is normally a waste of time in tiny files! I was hoping to get it under 2048 bytes raw and 1024 bytes gzipped; I achieved 2048 bytes raw, but after undoing some of the silly tricks gzipped is still 60 bytes off.Of course, if you decide to abandon AMD/CommonJS support, you can quickly whip 210 bytes (100 gzipped) off, but that’s a change in semantics so I can’t count that despite it getting it under 1000 bytes.
<div class=pell-actionbar
><button class=pell-button title=Bold onclick="document.execCommand('bold',!1)"><b>B</b></button
><button class=pell-button title=Italic onclick="document.execCommand('italic',!1)"><i>I</i></button
><button class=pell-button title=Underline onclick="document.execCommand('underline',!1)"><u>U</u></button
><button class=pell-button title=Strike-through onclick="document.execCommand('strikeThrough',!1)"><s>S</s></button
><button class=pell-button title="Heading 1" onclick="document.execCommand('formatBlock',!1,'<H1>')"><b>H<sub>1</sub></b></button
><button class=pell-button title="Heading 2" onclick="document.execCommand('formatBlock',!1,'<H2>')"><b>H<sub>2</sub></b></button
><button class=pell-button title=Paragraph onclick="document.execCommand('formatBlock',!1,'<P>')">¶</button
><button class=pell-button title=Quote onclick="document.execCommand('formatBlock',!1,'<BLOCKQUOTE>')">“ ”</button
><button class=pell-button title="Ordered List" onclick="document.execCommand('insertOrderedList',!1)">#</button
><button class=pell-button title="Unordered List" onclick="document.execCommand('insertUnorderedList',!1)">•</button
><button class=pell-button title=Code onclick="document.execCommand('formatBlock',!1,'<PRE>')"></></button
><button class=pell-button title="Horizontal Line" onclick="document.execCommand('insertHorizontalRule',!1,'<PRE>')">―</button
><button class=pell-button title=Link onclick="var s=prompt('Enter the link URL');s&&document.execCommand('createLink',!1,/^https?:\/\//.test(s)?s:'http://'+s)"></button
><button class=pell-button title=Image onclick="var s=prompt('Enter the image URL'));s&&document.execCommand('insertImage',!1,/^https?:\/\//.test(s)?s:'http://'+s)"></button
><button class=pell-button title=Undo onclick="document.execCommand('undo',!1)">↺</button
><button class=pell-button title=Redo onclick="document.execCommand('redo',!1)">↻</button
></div
><div class=pell-editor contenteditable oninput="typeof onChange=='undefined'||onChange(event.target.innerHTML)"></div>