One <span> per character in web-based text editor - javascript

I'm developing a web-based text editor without any contentEditable, textarea or input things. The biggest portion of my work is to measure widths of text on the left (right) side from the current caret position and moving the caret in the text.
For example when user presse the DOWN key a current left-offset of the caret must be computed and on the line below a character which's position is most similar must be found.
One very convenient way to do is to use one DOM element per character - I can just look at the offsetLeft property. Also, positioning the caret is much easier. Actually, everything is easier.
However I'm very unsure about the performance implications. I have seen this technique (or similar) used on some web-based JavaScript "IDE"s and it works just fine there.
Do you have any hints, tips?
Do you know some other fast way how to measure width of text. I want to avoid putting sections of a line to a DOM element and measuring its width each time as I think it will be much slower.
EDIT: I'm mostly asking about the main fact of EXISTENCE of many dom elements. How to do the measuring is a different thing.

I've seen this done (unfortunately can't find the link now) by using a canvas object and its measureText() method - basically you can ask a canvas "what size would this piece of text be if i rendered it in this style?" and use that to determine your caret position on the surrounding lines. This is performant, but of course it will only work in HTML5-capable browsers, and maybe not all of them.
But frankly this sounds like a big pain in the neck and probably more trouble than it's worth for an in-browser editor :)

You might be interested in this, which is a javascript implementation of the VI text editor. Unfortunately it does use a textarea, however not in the typical manner.

Related

Adding a significant amount of text elements in D3 causes performance issues

I'm developing a D3 application that utilizes a lot of text. Since there is alot of text elements, panning around using D3 zoom causes some lag. Is there a way in which I can improve the performance of my application? I'm happy to hear any suggestions, you guys might have. I've been thinking about paginating my data and detecting pan events but my UI is very complex and the user has the freedom of placing things the way he wants so I am not sure how to go about implementing a pan/pagination solution.
Such an open question is bound to get very opinion-based answers, and without more context you're also bound to get some useless ones, but here you go: it depends on what you want and what you mean by "a significant amount".
Supposing that significant amount is > 1000 elements, consider using canvas instead of SVG. Sure, if you can click on individual text nodes, that's more of a hassle, but it should be really good at panning/zooming.
If that is not possible, look at your code. Are you repositioning all text nodes individually? If so, place them all inside one g node and give that node a transform and zoom. In other words, make that node responsible for all global movement, and place the text nodes only relative to each other.
If that is also not possible, consider removing text nodes if they're outside the bounds of the SVG. Repositioning invisible nodes takes a lot of computation power, be smart about it. This is probably the most complex solution, so try it last.

HTML/JS Rich Text Editor Pagination

I'm looking on how to implement pagination/page breaks with page formats (A4, letter, etc.) using a rich text editor (like the Medium Editor).
The font family, font size, line height, margins are going to be fixed, as this is a very specific case study. I'm thinking of handling zoom levels in pure CSS (scale), instead of directly modifying widths, heights, etc.
Also, for the sake of the experiment, say I'll be running this in Chrome only & browser rendering differences aren't really an issue (but even if I were building this for various browsers, I'd try and use more precise units, such as "px", "em" for the font-sizes, page widths, margins between elements, etc. - probably just "px").
Keep in mind I'm not asking about "#page" rules or print rules, I know how to achieve what I want with those when I print out a PDF, but rather direct in-browser implementation. Printing should (and will) be handled by "#page" and I got no issue to handle page breaks there when I need them.
In the end, my question is - where do I start?
I imagine taking into account word-count and "h(1,2,3...)", "p" tag margins, along with case-specific CSS rules (break-after, break-word, break-line, etc) - even though taking those into account with js probably won't be very easy.
Probably even include the page height? Say, if the format is A4: 596px x 842px (72dpi) - take it into account when the total height of "each" element inside the page == height of page - [sum of bottom and top page margins]?
Other than the latter (with a simple js loop), if someone has any pointers, or maybe even a code snippet (or a plugin?), I'd be very grateful! Thank you!

Creating a custom web-based text editor using "span" elements - a bad idea?

I have an idea for a web application for which I would require full control over the functionality of the embedded text editors and the text editors must function exactly the same across all browsers. The standard contenteditable functionality is not sufficient to my needs on this occasion.
So I have been experimenting with various ways to implement a custom text editor. My first approach was to detect mouse clicks for caret insertion (though with no visible caret since there doesn't appear to be a way to achieve this). This worked rather well, but unfortunately there was no way to display the caret (aka flashing I-beam).
This means that my flashing caret must also be custom made. I can only think of two good ways to achieve this in a way which will be compatible across all browsers.
The first (and probably better) option would be to implement a custom layout engine in JavaScript much like Google have done with Google Docs.
The second solution (probably a lot easier) would be to encapsulate each character within its own <span> element and thus allowing the faux caret to be placed between specific characters. This does mean that there will be a LOT of span elements, but this would certainly achieve what I need whilst taking advantage of the browser layout engine. Another benefit with this approach is that I do not need to rely upon dodgy browser-specific text selection hacks.
So my question, is option #2 a really bad idea? If so, why?
First of all - do you really need to work on your own editor? There are Firepad and Etherpad with their pretty cool collaborative editing and perhaps more open source editors not based on contenteditable. It's really hard to create such editor, so it does not make sense to waste time on it.
However, if you really want to work on your own solution and you need exactly the same behaviour across all browsers, then you're doomed ;). Even if you'll avoid contenteditable there are definitely other things that can go wrong.
Anyway, the answer:
First option is very hard and time consuming at the beginning but it gives you a lot more power than the second one. E.g. having completely custom layout engine, you'll be able to implement page breaks without waiting for the CSS3's implementation (on which you will never could rely on, because you want exactly the same behaviour across all browsers). And in fact, you'll be able to bypass most of browsers' rendering differences. But, unless you've got a team of decent JS devs and few months (at least), I wouldn't even start thinking about that.
The second solution - reusing DOM is more realistic. I would perform some performance tests first, but having span per character would be easy to find out where the caret should be placed after mouse click. Without that it requires some trick... Which I don't know. You can try to check how Etherpad and Firepad (which uses Ace code editor) deal with that, but still - wrapping will be the easiest choice and at least on decent browsers it should not cause performance issues, unless you want to edit really long documents (but then you can start some optimizations).

Word Wrap in Raphael JS / SVG

How do you get words to wrap inside a box in RaphaelJS? Or in browser-based SVG in general?
I found this thread on it, but it doesn't make any sense. They say to use "widthToCharNum" but as far as I can tell, this thread is the only place those words have ever been used on the internet. They suggest using a "width" attribute, but this has no effect.
Text-wrapping is not built into Raphael or the SVG spec. Period. Coming from the HTML world, I found the absence of text wrapping pretty shocking.
However, you can do it yourself without too much difficulty. See this question for details and an example. Unfortunately, you have to burn some client-side cycles to make it work dynamically.
The svg.js library has a svg.textflow.js plugin. It's not ultra fast but it does the trick. It even stores overflowing text in a data attribute so you can use it to create continuously flowing columns. Here the text flow example page.
The tspan tag can give the illusion of word wrap, but there is no built in word wrap functionality.
The tspan tag is identical to the text tag but can be nested inside text tags and inside itself. Coupled with the 'dy' attribute this allows the illusion of word wrap in SVG 1.1. Note that 'dy' is relative to the last glyph (character) drawn. There is a tutorial on how to use tspan at http://tutorials.jenkov.com/svg/text-element.html.
The article at http://www.xml.com/pub/a/2002/09/11/quint.html might also be useful.
I know it's a little belated now, but you might be interested in my Raphael-paragraph project.
It's a small library that allows you to create auto-wrapped multiline text with maximum width and height constraints, line height and text style configuration. It's still quite beta-ish and requires a lot of optimization, but it should work for your purposes.
Usage examples and documentation are provided on the GitHub page.

Webkit columns find range of visible text

I have a piece of HTML which I am displaying inside a UIWebView using Webkit stylesheet attributes. I use Webkit to display the HTML in columns in order to simulate a book.
Only one column is visible at a time (one column represents one page). Now, I am trying to find the range of the visible HTML so that I can insert a span element right before the first visible word.
I managed to get the HTML element which contains the first visible word by using the JavaScript function, document.elementAtPoint(I might have the function name wrong), and changed its CSS class. but that just isn't accurate enough for me. I need it to be accurate up to the first visible word.
The idea is the create a column break at the first visible word when the fontsize is increased or decreased. I can using JavaScript to figure out in which column the element is, and programmatically scroll the user to that column, but first I need to get the element in there.
Can anyone help me?
The CSSOM View Module specification adds caretPositionFromPoint(x, y) to the Document interface, which returns a caret position for the specified x and y co-ordinates. WebKit supports caretRangeFromPoint, a close analogue from an earlier specification, which returns a Range.
It is possible that the word has been hyphenated and thus spans two columns, so rather than wrapping the first word in a span you may wish to consider the more naive approach of inserting the span directly at the cursor point. Here's an example:
var caretPos = document.caretRangeFromPoint(x, y);
if (caretPos)
caretPos.insertNode(document.createElement('span'));
Demo (WebKit only—click to insert spans): http://jsfiddle.net/Jordan/Aw9aV/
One final consideration: it is possible that WebKit will eventually stop supporting caretRangeFromPoint in lieu of caretPositionFromPoint; if so, you will need to adapt your code. Also note that the latter returns a CaretPosition which may not implement the insertNode method. The spec is still at WD, so be mindful that it is still in flux.
Ok, nog entirely sure what you are currently doing, but at the very least I should be able to give some useful tips, as I have some experience building page browsing systems in javascript.
First of all, in CSS3 you can define columns https://developer.mozilla.org/en/CSS3_Columns , which will automatically split up the content into different columns within a single element (where a single column has the full width of the uiwebview) and next add browsing controls which move the entire element containing the element (using css3 3d translations for smooth hardware accelerated motion and you know the width of the columns so you don't need to worry about what the first word on the page is). In which case you don't need to worry about splitting up the column breaks yourself. (Though, as I said, I am not sure to what extend you are already doing this).
Alternatively you may decide to wrap all your content in small inline-blocks (as older column implementations did) or even up to the point of single inline elements, each containing a single word. (Though this doesn't seem necessary anymore)
Lastly, work is being done on http://www.w3.org/TR/css3-regions/ which will make this even easier in the future, but for now it's only available in chrome and ie10
On the other hand, you might already be doing this or I might be missing the point, in which case I would need to see some code before I can give you a more specific answer. (I can think of various javascript tricks to work with letters within a text, but none seem necessary in your case)

Categories