I'm interested in developing a word processor type application with a structure like this:
Document -> pages -> paragraphs
I essentially want to get to a point where I'm able to do something like this:
var page_4_content = document.pages[4].paragraphs;
This document would need to be contenteditable. I've already built a rudimentary version using jQuery but it is slow as hell in processing all the paragraphs on a 100 page document. I've been looking at alternatives and was wondering if Vue will give me better performance and an easier way to manage the document content instead of having to use selectors all the time?
Specifically, pages have a fixed height so if adjusting an element causes the page height to change, the whole document needs to be repaginated and this locks up the browser.
I'm wondering if Vue's use of a virtual DOM will alleviate that problem?
Many thanks!
Related
I have the following problem:
A have a web application where I regularly need to update the user interface when data changes. The data consists of a list of items with different attributes. Because the UI representations of these items can be complex, I use JS templating to render them. When they change I just replace them in the DOM with the HTML representing their updated state.
This approach is simple but has several problems:
you need to re-attach all event handlers because you practically replace elements
there is a flickering effect when reloading resources (probably can be solved using document fragments)
it's impossible to work with developer tools (inspector) if the content changes frequently because, again, all the elements are replaced
So I was wondering if there is any JS templating engine of that many that can deal with the situation. I'm thinking of a feature that intelligently matches elements of the new render and an old one and only changes the content when it has really changed.
I'm thinking of something like this:
Old HTML
<div>
<h1>TV</h1>
<span>$250</span>
Add to cart
</div>
New HTML
<div>
<h1>TV</h1>
<span>$260</span>
Add to cart
</div>
The templating engine find the <span> in the original DOM and replaces its changed value but leaves the rest of the elements intact.
Finally I came across Rivets.js which a lightweight, highly extensible JavaScript templating engine with real time data binding. I love it so far, it's exactly what I needed.
you can try the AngularJS
A simple example:http://jsbin.com/opayuf/4/edit
you can check out these examples if they can meet your requirements
http://tutorialzine.com/2013/08/learn-angularjs-5-examples/
HandleBarsJs may be the one you need, you could see the discussion on
Handlebars.js: Use a partial like it was a normal, full template
You can also try Ember js based on HandlerBarsJs, you can check is out
http://emberjs.com/guides/templates/handlebars-basics/
http://emberjs.com/guides/templates/rendering-with-helpers/
Disclaimer:
I've blathered on kind-of excessively here in an attempt to provide enough context to pre-empt all questions you folks might have of me. Don't be scared by the length of this question: much of what I've written is very skim-able (especially the potential solutions I've come up with).
Goal:
The effect I'm hoping to achieve is displaying the same element (and all descendants) in multiple places on the same page. My current solution (see below for more detail) involves having to clone/copy and then append in all the other places I want it to appear in the DOM. What I'm asking for here is a better (more efficient) solution. I have a few ideas for potentially more efficient solutions (see below). Please judge/criticize/dismiss/augment those, or add your own more-brilliant-er solution!
"Why?" you ask?
Well, the element (and it's descendants) that I'm wanting to display more than once potentially has lots of attributes and contents - so cloning it, and appending it someplace else (sometimes more than one other place) can get to be quite a resource-hogging DOM manipulation operation.
Some context:
I can't describe the situation exactly (damn NDA's!) but essentially what I've got is a WYSIWYG html document editor. When a person is editing the DOM, I'm actually saving the "original" node and the "changed" node by wrapping them both in a div, hiding the "original" and letting the user modify the new ("changed") node to their heart's content. This way, the user can easily review the changes they've made before saving them.
Before, I'd just been letting the user navigate through the "diff divs" and temporarily unhiding the "original" node, to show the changes "inline". What I'm trying to do now is let the user see the whole "original" document, and their edited ("changed") document in a side-by-side view. And, potentially, I'd like to save the changes through multiple edit sessions, and show 'N' number of versions side-by-side simultaneously.
Current Solution:
My current solution to achieve this effect is the following:
Wrap the whole dang dom (well, except the "toolbars" and stuff that they aren't actually editing) in a div (that I'll call "pane1"), and create a new div (that I'll call "pane2"). Then deep-clone pane1's contents into pane2, and in pane1 only show the "original" nodes, and in pane2 only show the "changed" nodes (in the diff regions - everything outside of that would be displayed/hidden by a toggle switch in a toolbar). Then, repeat this for panes 3-through-N.
Problem with Current Solution:
If the document the user is editing gets super long, or contains pictures/videos (with different src attributes) or contains lots of fancy styling things (columns, tables and the like) then the DOM can potentially get very large/complex, and trying to clone and manipulate it can make the browser slow to a crawl or die (depending on the DOM's size/complexity and how many clones need to be made as well as the efficiency of the browser/the machine it's running on). If size is the issue I can certainly do things like actually remove the hidden nodes from the DOM, but that's yet more DOM manipulation operations hogging resources.
Potential Solutions:
1. Find a way to make the DOM more simple/lightweight
so that the cloning/manipulating that I'm currently doing is more efficient. (of course, I'm trying to do this as much as I can anyway, but perhaps it's all I can really do).
2. Create static representations of the versions with Canvas elements or something.
I've heard there's a trick where you can wrap HTML in an SVG element, then use that as an image source and draw it onto a canvas. I'd think that those static canvasses (canvi?) would have a much smaller memory footprint than cloned DOM nodes. And manipulating the DOM (hiding/showing the appropriate nodes), then drawing an image (rinse & repeat) should be quicker & more efficient than cloning a node and manipulating the clones. (maybe I'm wrong about that? Please tell me!)
I've tried this in a limited capacity, but wrapping my HTML in SVG messes with the way it's rendered in a couple of weird cases - perhaps I just need to message the elements a bit to get them to display properly.
3. Find some magic element
that just refers to another node and looks/acts like it without being a real clone (and therefore being somehow magically much more lightweight). Even if this meant that I couldn't manipulate this magic element separately from the node it's "referencing" (or its fake children) - in that case I could still use this for the unchanged parts, and hopefully shave off some memory usage/DOM Manipulation operations.
4. Perform some of the steps on the server side.
I do have the ability to execute server side code, so maybe it's a lot more efficient (some of my users might be on mobile or old devices) to get all ajax-y and send the relevant part of the DOM (could be the "root" of the document the user is editing, or just particularly heavy "diff divs") to the server to be cloned/manipulated, then request the server-manipulated "clones" and stick 'em in their appropriate panes/places.
5. Fake it to make it "feel" more efficient
Rather than doing these operations all in one go and making the browser wait till the operations are done to re-draw the UI, I could do the operations in "chunks" and let the browser re-render and catch a breather before doing the next chunk. This probably actually would result in more time spent, but to the casual user it might "feel" quicker (haha, silly fools...). In the end, I suppose, it is user experience that is what's most important.
Footnote:
Again, I'm NDA'd which prevents me from posting the actual code here, as much as I'd like to. I think I've thoroughly explained the situation (perhaps too thoroughly - if such a thing exists) so it shouldn't be necessary for you to see code to give me a general answer. If need be, I suppose I could write up some example code that differs enough from my company's IP and post it here. Let me know if you'd like me to do that, and I'll be happy to oblige (well, not really, but I'll do it anyway).
Take a look at CSS background elements. They allow you to display DOM nodes elsewhere/repeatedly. They are of course they are read-only, but should update live.
You may still have to come up with a lot of magic around them, but it is a similar solution to:
Create static representations of the versions with Canvas elements or something.
CSS background elements are also very experimental, so you may not get very far with them if you have to support a range of browsers.
To be honest, after reading the question I almost left thinking it belongs in the "too-hard-basket", but after some thought perhaps I have some ideas.
This is a really difficult problem and the more I think about it the more I realise that there is no real way to escape needing to clone. You're right in that you can create an SVG or Canvas but it won't look the same, though with a fair amount of effort I'm sure you can get quite close but not sure how efficient it will be. You could render the HTML server-side, take a snapshot and send the image to the client but that's definitely not scalable.
The only suggestions I can think of are as follows, sorry if they are long-winded:
How are you doing this clone? If you're going through each element and as you go through each you are creating a clone and copying the attributes one by one then this is heaavvvyy. I would strongly suggest using jQuery clone as my guess is that it's more efficient than your solution. Also, when you are making structural changes it might be useful to take advantage of jQuery's detach/remove (native JS: removeChild()) methods as this will take the element out of the DOM so you can alter it before reinserting.
I'm not sure how you'v got your WYSIWYG, but avoid using inputs as they are heavy. If you must then I'm assuming they don't look like inputs so just swap them out with another element and style (CSS) to match. Make sure you do these swaps before you reinsert the clone in to the DOM.
Don't literally put video at the time of showing the user comparisions. The last thing we want to do is inject 3rd party objects in to the page. Use an image, you only have to do it while comparing. Once again, do the swap before inserting the clone in to the DOM.
I'm assuming the cloned elements won't have javascript attached to them (if there is then remove it, less moving parts is more efficiency). However, the "changed" elements will probably have some JS events attached so perhaps remove them for the period of comparision.
Use Chrome/FF repaint/reflow tools to see how your page is working when you restructure the DOM. This is important because you could be doing some "awesome" animations that are costing you intense resources. See http://paulirish.com/2011/viewing-chromes-paint-cycle/
Use CSS over inline styling where possible as modern browsers are optimised to handle CSS documents
Can you make it so your users use a fast modern browser like Chrome? If it's internal then might be worth it.
Can you do these things in Silverlight or Adobe Air? These objects get special resource privileges, so this will most likely solve your problem (according to what I'm imagining the depth of the problem is)
This one is a bit left-field but could you open in another window? Modern browsers like Chrome will run the other window in its own process which may help.
No doubt you've probably looked in to these things more than I but good luck with it. Would be curious how you solved it.
You may also try: http://html2canvas.hertzen.com/
If it works for you canvas has way better support.
In order to get your "side-by-side" original doc and modified doc.. rather than cloning all pane1 into pane2.. could you just load the original document in an iframe next to the content you are editing? A lot less bulky?
You could tweak how the document is displayed when it's in an iframe (e.g. hide stuff outside editable content).
And maybe when you 'save' a change, write changes to the file (or a temp) and open it up in a new iframe? That might accomplish your "multiple edit sessions"... having multiple iframes displaying the document in various states.
Just thinking out loud...
(Sorry in advance if I'm missing/misunderstanding any of your goals/requirements)
I don't know if it's already the case for you but you should consider using jQuery library as it allows to perform different kinds of DOM elements manipulation such as create content and insert it into several elements at once or select an element on the page and insert it into another.
Have a look on .appendTo(), .html(), .text(), .addClass(), .css(), .attr(), .clone()
http://api.jquery.com/category/manipulation/
Sorry if I'm just pointing out something you already know or even work with but your NDA is in the way of a more accurate answer.
I am building a one page webapp and it's starting to get pretty big. There are several components to the app, each one meticulously styled. On average the app has a DOM element count of 1200+. I have been warned by my YSlow scan that this is too many, and that I should have no more than 700 DOM elements.
I am usually quite strict and efficient with my markup and I doubt I would be able to trim much off. I tend to use a lot of DOM elements to get the styling exactly right and working cross browser.
How can I dramatically cut the number of DOM elements?
Will I have to load more of the content on demand (ajax) instead on all on page load?
Does a large amount of DOM elements have a big impact on performance?
I would love to hear people's experience with this and any solutions you may have...
The number of dom elements would only enter into the picture if you're doing a lot of DOM and/or CSS manipulation on the page via Javascript. Scanning for an ID in a page with 50,000 elements is always going to be slower than a page with only 500. Changing a CSS style which is inherited by most of the page will most likely lead to more redrawing/reflowing than it would on a simpler page, etc...
The only way to cut element count is to simplify the page.
We've built a single page web app. Initially Yslow worried me as we had 2,000+ DOM objects in the page.
After some work we got all the other Yslow items to green. And we ended up living with it(around 1,800 right now) as the app is very fast in various browsers.
But we don't support IE6 and IE7, and it could be different for these browsers.
How can I dramatically cut the number of DOM elements?
By using only those elements that are necessary. If you want an more elaborate advice, post your code.
Will I have to load more of the content on demand (ajax) instead on all on page load?
If you want your page to perform better on start-up, you can do that.
Does a large amount of DOM elements have a big impact on performance?
Not necessarily.
You can render elements on demand when user click a button or can use lazy loading like Twitter.
I have a question about Javascript widgets. The widget I am working on simply embeds content on a page instead of using iframes. So far it looks good. But there are cases where some users layouts are messing up the widget. For example, the widget might require a width of 300px to appear. But the parent div is set to 250px and hence the right part of the widget is cut off.
I was wondering what sort of precautions should be taken to prevent this? I was talking to the product manager who mentioned he wanted me to check the parent div elements and get the size and then show an alternate message if their size is not accurate. But again, since this is Javascript and the widget is supported in many diff browsers(including IE6), I am wondering how fail-safe this method would be? What if I need to iterate the DOM all the way up before getting a valid size? I am also worried about performance here. This extra checks would slow down the delivery of my widget content to "good users" since I am adding a layer of complexity to all users. I don't want to penalize good users just because of the few errant ones.
I am not using any sort of JS library here, so any solution should not suggest the use of one. Also, the reason for not using a library was simply not to add extra weight to the page load to deliver a widget. I understand that "jquery" for example is small, but in my case, even 24k compressed seems like an overkill for a widget delivery that contains no core code for the widget.
Has anyone dealt with such issues before? What are your solutions to these?
There are reliable ways of determining the size of an element using JavaScript. You're quite right that you may need to iterate up the tree in some cases, but the answer you get will ultimately be quite valid.
Although you don't want to directly include any library code in this project, you may consider looking at how the major libraries implement their "what's the width of this element" functions to drive your own implementation.
Beware of quirks mode too.
I'd check to see of the page has Jquery, if not load it into the page using no-conflict mode. Then use jQuery to examine the page.
See: How to embed Javascript widget that depends on jQuery into an unknown environment
(excuse me if this is not the right forum to post - i couldn't find anything related to non-native programming and related to this topic)
I Am trying to set a dynamic HTML into an iFrame on the webpage. I have tried a couple of things but none of them seem to work. I m able to read the innerHTML but can't seem to update it.
// Able to read using
document.getElementById('iFrameIdentifier').innerHTML;
// On Desktop IE, this code works
document.getElementById('iFrameId').contentWindow.document.open();
document.getElementById('iFrameId').contentWindow.document.write(dynamicHTML);
document.getElementById('iFrameId').contentWindow.document.close();
Ideally the same function should work as how it works for div's but it says 'Object doesn't support this method or property".
I have also tried document.getElementById('iFrameId').document.body.innerHTML.
This apparently replaces the whole HTML of the page and not just the innerHTML.
I have tried out a couple of things and they didn't work
document.getElementById('iFrameId').body.innerHTML
document.frames[0].document.body.innerHTML
My purpose is to have a container element which can contain dynamic HTML that's set to it.
I've been using it well till now when I observed that the setting innerHTML on a div is taking increasing amount of time because of the onClicks or other JS methods that are attached to the anchors and images in the dynamic HTML. Appears the JS methods or the HTML is some how not getting cleaned up properly (memory leak?)
Also being discussed - http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_26185526.html#a32779090
I have tried a couple of things but none of them seem to work.
Welcome to IEMobile! Nothing you know about DOM scripting applies here.
Unfortunately, cross-iframe scripting does not appear to be possible in IEMobile6-7.
frameelement.contentDocument (the standard DOM method) isn't available
frameelement.contentWindow.document (the IE6-7 workaround version) isn't available
the old-school Netscape window.frames array only works for frames, not iframes
having the child document pass up its document object to the window.parent only works for frames, not iframes. In an iframe, window.parent===window.
So the only ways forward I can see are:
use frames instead of iframes. Nasty. Or,
use document.cookie to communicate between parent and child: the child document is just a script, that checks for a particular cookie in document.cookie on a poller, and when it's found that's a message from the parent, and it can write some HTML or whatever. Slow and nasty. Or,
using the server-side to inject content into the frames, passing it in as an argument to a script. Slow, nasty, and potentially insecure. Or,
avoid frames completely (best, if you can). Or,
drop support from IEMobile6-7 (best for preserving your sanity, if you can get away with it!)
Appears the JS methods or the HTML is some how not getting cleaned up properly (memory leak?)
Yes, probably. IEMobile6-7(*) is close to unusable at dynamic HTML. It gives you a lovely flavour of what scripting used to be like for us poor gits back in the Netscape 4 days.
Try to avoid creating and destroying lots of nodes and event handlers. Keep the page as static as possible, re-using element nodes where possible and setting text node data properties in preference to tearing everything down and making anew with createElement or innerHTML. Use an event stub (onclick="return this._onclick()") in the HTML together with writing to _onclick if you need to set event handlers from JavaScript, in preference to recreating the HTML with a new event handler (or just trying to set the property, which of course doesn't work in IEMobile). Avoid long-running single pages when you can.
It'll still crash, but hopefully it'll take longer.
*: that is, the versions of IE present on WinMo before version 6.1.4, where it became the infinitely better IEMobile8, marketed as “Internet Explorer Mobile 6” (thank you Microsoft).
Okay, I kinda resolved the issues that i was facing earlier and the bigger issue which was setting HTML to an iFrame on IEMobile. But i still have one more PIA which is related to double scollbars - which i am currently looking into. There seems to be more poor souls facing similar problem - if i fix that too. I will post an update here.
How did i finally write to iFrame on IEMobile?
Have 2 divs one to wrap the iFrame and the other to write inside an iFrame.
document.getElementById('OuterDiv').innerHTML = '';
document.getElementById('OuterDiv').innerHTML = '<iframe id="iFrameId" src="somefile.html"></iframe>';
This creates an iFrame each time and in the somefile.html on load there is a InnerDiv.innerHTML which doesn't seem to leak the memory.
In the somefile.html there will be an onLoad method which will fetch the HTML (explained below on how i managed to get it) and do a
document.getElementById('InnerDiv').innerHTML = dynamicHTML;
How did I manage to pass the HTML between parent and child iFrame
As well explained by #bobince earlier, one has to rely on 3rd party service like a cookie or a server to pass around the data between parent and the child iFrame.
I infact used an ActiveXControl to set and get data from the parent and child iFrame's javascript respectively. I won't recommend doing this if you have to introduce an ActiveX Control just for this. I accidentally already have one which I use to get the Dynamic HTML in the first place.
If you need any help you can DM me - Twitter #Swaroop
Thanks #bobince for your help. I am marking this one as an answer because it says what i did to fix the issue.