Alter style in JavaScript without affecting what gets rendered in the browser - javascript

I extract several blocks that are spread across the page, draw them onto a canvas, via Canvas​Rendering​Context2D.draw​Image(), and then I export the canvas to PDF/JPG.
Now, while drawing them, I also want to tidy their looks and/or remove/display parts of some blocks, because the styling no longer makes sense from a static point of view.
I can append a class on body, style blocks differently while body has that class, build the canvas, export it to a content type of choice and remove body class, but this makes the page look quirky for 1 - 2 seconds. (I could just cover the page with a loader, I guess)
I can clone the block, style it differently or apply whatever styling (inline or via classes) and draw an image from that, but this is not optimal.
Any idea on how to do this properly? I'm inclined to go for something like emulation. Have the whole "export styling" under the native #print and emulate that before drawing the blocks onto the canvas, without affecting what gets rendered in the browser, but I'm not sure if this is possible.

I know these don't answer your specific question, but perhaps these are viable alternatives to the problem.
It sounds like you're trying to make something printable. I assume this is triggered by a user interaction of some kind so...
This means you have a few interface options. For example, you could "hide" the screen by placing a modal over the entire thing with a message that says "processing just a moment". Then the body (or another element) class solution works.
You could copy the elements as you suggested. If you go that route I would move the copies off the screen while you change them.
.element {
position: fixed;
left: -100vw;
}
Without knowing how many duplicates you need to make it's hard to recommend this option.
Alternately, could you offload the effort to a service worker? This would require a copy into memory BUT its completely detached from the DOM and runs in a different thread.
It's a really interesting problem though!

I think you can use node.cloneNode(true) to make a deep clone for all these blocks, put them in a classname scoped common root that's hide away from user's view, then you mod their style secretly.
Since you mod the cloned version of nodes, the original remains untouched. Plus, using the classname scope, your css can target these clones accurately.

Related

JS/DOM optimization: using :after content styling instead of altering innerHTML

Basically, given that all possible contents of an element are known in advance, you can use a bunch of :after content styles instead of altering the innerHTML of the element. Just change the class to get new content.
My profiling in Chrome indicates that this does improve performance a bit, but I'm not entirely sure, as the cost might then be hidden somewhere else? And I'm not sure if it's a bad idea for other browsers besides Chrome. Any thoughts on this?
Please note that the context here is for 60 frames per second simulations/visualisations, so in this context 2 vs 4 milliseconds is a big and significant difference, which it might not be in the context of a page load.
Example styles for numbering can be seen here: https://github.com/magwo/elevatorsaga/blob/2fa5dc0c0397d0565ce5dcc45c68b19d924a4955/style.css#L290
pseudo-elements are design elements they are not content, they don't appear in the source code, they don't exist in the DOM, they wont be crawled by search engines, you cannot select or copy them, and of course, they don't exist without CSS, which is a style-sheet.
It may be faster, but that is probably because it is not content. And maybe it is easier for the browser to change a class than change the content of an element.
The question is, do you want to change the content, or the visual representation of it?
As for the implementation, I believe that having every possible content loaded in to a CSS is not an optimization, the same thing applies if you are planning to request a new CSS file for every new content change.
Finally, at least for now, you can't style individual parts of the content of a pseudo-element, you can't add paragraphs,spans and other markup.

Is it always necessary for overlay elements to be located in end of HTML body?

I have noticed that in javascript frameworks elements such as dialogs, tooltips and alerts mostly appear at end of body.
I'm making my own implementation of these elements and trying to make it failproof. I'm repeating some techniques like using transparent iframe to overlay embeded objects in old browsers, and so on.
What restrictions could I face if I place my dialog/tooltip somewhere deep inside of the DOM tree with {position: fixed}? I'm afraid if there are some dangers to this approach, because big frameworks never use it.
I want to support IE8+.
Aside from z-ordering that is a very valid point made by Teemu, another major consideration in JS frameworks is speed of execution / speed of lookup.
The DOM in JS terms is one large object. The deeper into an object javascript needs to go to get what it's being asked for, the less performant the script gets, take a look at this answer.
Therefore it makes sense to keep everything that is probably going to be cloned or deep copied at a sensible nesting level and in the correct z-order. That happens to be toward the end of the body and usually wrapped by at most one containing element.
There may be other reasons but the depth / nesting sprung to mind as a consideration I'd take into account.
Short answer - very few techniques like this are "always necessary". JavaScript can easily remove items from their natural position in the DOM and relocate them at will.
Long answer - I don't think approaching this from a JavaScript first angle is correct. Look at it in terms of where the content belongs naturally within the hierarchy of the rest of the DOM.
For example, if you are talking about a modal dialog, then the chrome (the container elements) usually do not belong within the rest of the DOM - they exist only to contain and provide modal overlay functionality for the content within. This chrome does not participate in the outline of the DOM and the rest of the content. In that case, unless you are able to load them separately via ajax or embed the chrome HTML within the JavaScript, then the closest you will come to removing them from the main DOM is to append them to the bottom of the main DOM content. Note that this disregards the upcoming TEMPLATE element (http://www.html5rocks.com/en/tutorials/webcomponents/template/) which is designed for just this purpose.
However, the content of your dialog might very well belong within the main content of the DOM - either as an element, or as an attribute (i.e. title or data-) to an associated element. This would especially be true for tooltip text.

How to draw a graphic outside the browser/document?

A line graph is necessary for the purpose of my extension in an web game online.
I want to do something like this:
The intention is that this graph should be OUTSIDE of the DOM/browser, because if I put this inside the game document, they will know that this line has been put into the DOM with a simple call $("#rareLineGraph").length > 0 and they will detect it, and they should not know.
I tried it with frames, but are very uncomfortable (windows)
Some suggestions please ?. Thank you very much
You can in principle draw outside the browser with a separate program operated via Native Messaging, but that would be quite difficult and over-complicated. That is, however, the only approach that fully corresponds to your requirements.
As a suggestion, you can hide your graph from such a simple inspection by using a random ID, or even skipping using an ID and just keeping a reference to the created element in a variable. Also, inserting your node into random places in the document structure and using absolute positioning will obfuscate it further. It will be harder (but not impossible) to detect.
Other than that, I don't think there are many ideas that can help. Chrome renderer looks only at the DOM, and there's no API to create any kind of overlay. DOM can be hidden from the outer document with Shadow DOM techniques, but as far as I know the shadow root element will still be visible to the page.

Randomize CSS Rules

I was working on a website, and I thought of how funny it would be if I completely randomized the CSS rules on the page as a joke. (Not just the elements' styles, but the CSS rules themselves, because the website has a lot of dynamically created elements.) Each time you loaded the page the result would be completely different, and most of them would look terrible. So my question has two parts:
Using JavaScript/JQuery, How do you programmatically get a list of all CSS rules? As a sort of dictionary, with the rules paired to the selectors.
Then, after you have broken down the list and randomly assigned each rule to a different selector, how do you delete the previous rules and substitute in your own?
NOTE: I mean, using JavaScript/JQuery, how do you randomize the rules on the client side, not just a single CSS file.
You can access and traverse all the stylesheets with document.styleSheets. See the API documentation on MDN
Mind you this is a bit psuedo-ey, also note that you can do this using pure JS.
foreach (var e in document.getElementsByTagName("*")) {
foreach (var p in el.style) {
var r = Math.random(0, 255);
e.style[p] = r;
}
}
also note that not all css properties take 0 - 255 so you might have to create your own algorithm, but this'll sure get you started.
Looping through all the elements in the page would be trivial (document.getElementsByTagName('*')).
Looping through all the styles available for each element would be trivial (element.style).
Setting random values for any given style would be harder.
You would need to have hard-coded lists of styles and possible values for each of them, because the values that can be set vary so wildly. Some can be in a variety of different units (px, em, %). Some of them have pre-defined keywords as the possible values.
And a random value is no good at all if you don't have limits to it. Setting a random width sounds easy, but you have to know what ranges you're going to work to. width:5743731px isn't going to be very useful even for a randomised page.
And then you have the properties that can fetch external resources. A CSS background image is going to be virtually impossible to randomise, and fonts would need to be loaded in a separate #font-face declaration, so you would only be able to randomise fonts that you know are loaded.
And then you have to think about how randomised you're going to be. Are you going to randomise every possible style on every element? (crazy, but hey, this whole thing is crazy so why not) Or just one style per element?
Don't forget that a lot of styles work in conjunction with each other. So text-overflow:ellipsis does nothing unless you also have white-space:nowrap and overflow:hidden. And setting a border-color is pointless unless you've also set the other border attributes.
So yes, I think your first task here would be to go through the list of CSS styles and work out which ones could be randomised and what the possible randomised values for them could be. That's the difficult bit. Once you've got that, hard code it into your program, and the rest should be fairly simple.

Link Elements to Keep Their Appearance In Sync

I'm developing an iPad app using PhoneGap and jQuery Mobile and I'd like to create a preview pane in a carousel. The preview pane would include a smaller version of each of the other panes, scaled so they fit inside the single pane. The panes are not static and can be updated at any time using WebSockets, and the preview should be updated simultaneously. There can also be any number of panes (although to keep things simple, assume an upper limit of 9). For performance purposes, assume each pane can have upwards of 200 DOM objects attached to it. To make it slightly more complicated, the carousel can exist on more than one different page.
I've been contemplating the best way to go about implementing this preview pane, and, before inventing a pair of Complicator's Gloves, would like to hear back from the community on any possible better strategies.
A couple methods I have been considering include:
Cloning each pane and then using a CSS transform to scale it to an appropriate size, based on how many panes there are, and then attaching the clones to the preview pane.
Store each pane as a jQuery object in a variable and draw each pane and the preview pane using that object (possibly necessitating redrawing the entire carousel every time there is an update, depending on how much effort I want to make identifying and updating deltas).
Repositioning all the panes so that they exist inside the preview pane when the preview pane is active (this might break the carousel, or at least make it look slightly bizarre as a user swipes a pane over but hasn't actually moved on to that pane yet).
Is there anything I'm missing? It would be nice if there was an easy way to "link" two elements together to make one mirror the other, but apply different CSS to one or the other (for zooming). I suppose it might be possible to do this by creating an event that would fire and then adding a listener to its clone, which would then copy the html of the updated element to itself (probably wouldn't be too terribly to difficult to write a jquery plugin to manage this).
Any better suggestions?
I am not sure what phonegap allows for as far as rendering options go, but my first instinct would be to take a screen shot of the relevant pane. Perhaps phonegap has this built in?
Another option is a javascript library which will clone the DOM and create an HTML5 canvas element. You can either then display the canvas natively, or convert the canvas data in to image data.
Here is one such library: http://html2canvas.hertzen.com/
Given the large number of elements needed, I would hesitate to clone those over and over again. However, if live previews are a necessity, that might be more efficient than using image files or the canvas. You could fire off the canvas draw function after major changes, but probably wouldn't want to do it after the end of every frame of animation.

Categories

Resources