I load some style.css dynamically in javascript. when the style is loaded, I'll do some calculation that depends on the styles. The code is like this:
link = document.createElement('link');
link.href = 'sheety.css';
link.rel = 'stylesheet';
document.head.appendChild(link);
link.onload = function(){
//some code
//here, I can't the the correct style sometime.
//but most of the time, I can get the correct style
setTimeout(function(){
//here, I can also get the correct style
}, 1000);
};
So, my question is: when does the style is applied successfully?
If I modify the element's position(width, height), can I get the correct value instant, or I must wait some time to let the browser do something by using like setTimeout() function?
I read some resources that says: call dom.getComputedStyle will cause a browser reflow.
I indeed call this method, but I still get the wrong width/height of the DOM.
When I debug the JS code in chrome, I can see that the style is not applied at the break point (from the style tab in chrome developer tools).
Once the style sheet is loaded, a CSSOM or CSSObjectModel is generated. Along with the DOM, the CSSOM is rasterized by a rasterizer and applied (in Chrome, done by Skia).
A style can be said to be applied successfully when a browser completes a reflow or a repaint. We can force the browser for a redraw using many techniques:
element.hide().show(); #using jQuery
Inserting a node into the DOM
and removing it.
These techniques are better explained in a similar question.
Related
Inserting dynamic SVG content into the DOM does not work as expected, having the SVG element onload attribute (containing JavaScript) regarding: "setInterval()".
As noted in the search tags of this question; this is plain (valilla) JavaScript (not jQuery); here's a breakdown of the issue:
I have some SVG code (plain text) that gets inserted into a <div> as innerHTML
the SVG element has an onload attribute with some JavaScript inside it
the JavaScript contains setInterval(...) - (which does not work at all)
I grab the SVG element from the temporary div and return it as the result of a function.
this result is appended into some element in the live DOM (body)
the strange issue:
any other code inside that onload attribute works fine,
only setInterval & setTimeout is completely ignored
More info:
During runtime (start-up), the SVG code is grabbed from an existing embed element .getSVGDocument() (after it has loaded) and prepared as plain HTML which can just be used as a template to create many others from the same source-code. I'm not using cloneNode(true) -because: the interval is for animation (continuous slow & smooth rotation) - which could have a heavy impact on client-side resources, hence, I thought it best to grab the code and keep it as template - then remove the original from the DOM.
With all the above in mind, everything works fine:
The (new) SVG shows up on screen, all nice and dandy-like
When I console.log the (inline) SVG code that is used, all looks perfect
I get no errors, and there is no error handler that mutes errors (window.onerror == null)
The JavaScript (text) inside the SVG node's onload attribute works for things like: console.log(this) - (which produces the SVG element in the log) - however, as mentioned: setInterval() & setTimeout() is just simply ignored - without any error and no warning.
The following code is a very short example, and (regrettably) it works; however, in my production app it doesn't.
The code:
var html = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" onload="setInterval(function(){ console.log(\'testing\'); },500);">';
var temp = document.createElement('div'); temp.innerHTML = html;
var node = temp.getElementsByTagName('svg')[0];
document.body.appendChild(node);
If you test the above code in a new js file, it works; however, for the life of me I can't find the reason why it breaks in my app; as explained, it's quite simple really.
The question:
Does anyone know if there is some "gotcha" I'm not aware of regarding this? Maybe name-spacing?
If the source-code is required, I can load it up on JSFiddle, or CodePen -if required, but, it's a lot of code, and many files, which may not be necessary for publication.
I'm sure it's just something small; like, how timers register according to scope, and maybe how it's affected in .bind() ?
I'm really stuck with this, and I kinda need it working for a good impression for a job-interview; so if you know anything that could help, I would appreciate your input very much.
Thank you.
embedded content, onload attributes & the DOM
The following may help in related scenarios:
when targeting an asynchronous source, make sure the contentDocument or getSVGDocument() contains the resources you need to access. The window.onload, or the DOMContentLoaded event is relative to the current DOM, so it may help constructing your own listener->trigger for a cross-browser solution, because the contents you need may not be ready in a synchronous fashion.
the onload attribute/event is not triggered when inserting dynamic content that is not asynchronously loaded, but may fire under certain circumstances, so, again, a custom:
listen->trigger will solve that.
question specific
The question is directly related to the 2nd point above, and the answer is quite simple really:
in the "onload" attribute of said SVG, set a simple value as property of this like:
<svg onload="this.ready = true; setTinterval(...)"
in the constructor function, after the element was dynamically created, simply check if the svg-node's onload event was fired like so:
if (!svgNode.ready){ svgNode.onload(); }
If there is an error in your code, but no error is shown, make sure window.onerror is either null -or if it's a function, make sure it does NOT return true - else it may suppress errors and you'll have a hard time tracking down what's wrong.
Please feel free to either improve this answer, or comment and I'll improve it accordingly; however, better answers will be appreciated.
6 years later...
With vanilla JavaScript Web Components you can do:
<load-svg></load-svg>
<script>
customElements.define("load-svg", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<svg></svg>`;
setInterval(() => {
console.log("testing");
}, 500);
}
});
</script>
I have inline css in my element which i do not want. There is a lot of js in my website and i do not know from where this css is coming.
Any help will be great.
Thanks in advance.
If I were you, I'd use removeAtrr() to remove all the inline style for that element:
$('#yourElementId').removeAttr("style");
After that, I'll set any style again through external css file or javascript if necessary.
Or if you want to override the inline styles, you can also try to use !important attribute in css.
I think the most solid way to do it would be to use the browser's dev tools to run through the js one line at a time. This will show you the exact point in the code execution where the style is added. Here's a link demonstrating how to use breakpoints: https://developers.google.com/chrome-developer-tools/docs/javascript-debugging#breakpoints
Otherwise, if you're confident the code will be jQuery functions, do a search for .css(. If it's possible the change is made without jQuery, search for .style.. Some other possibilities are fadeIn, fadeOut, and animate. Once you find any of these, you can track what element they are being applied to to determine if they are relevant to the element you want to change.
Here are some selectors to look out for (vanilla JS and jQuery)
ClassName
document.getElementsByClassName('some-class');
$('.some-class');
ID
document.getElementById('some-id');
$('#some-id');
TagName
document.getElementsByTagName('tagNameHere');
$('tagNameHere');
QuerySelector
document.querySelector('cssSelectorHere');
document.querySelectorAll('cssSelectorHere');
$('cssSelectorHere');
If the problematic CSS is set with jQuery, you can hook into jQuery's cssHooks API to see when a particular CSS is being set. For example, if the problematic CSS is "margin-right" you can detect when it is being set and throw an exception so you can trace it through the browser's debugger:
var targetElement = document.getElement("checkme");
$(function() {
$.cssHooks["marginRight"] = {
set: function(node, value) {
if(node == targetElement) {
throw "stop that!";
}
else node.style.marginRight = value;
}
};
});
I'm trying to precisely fit a string into a certain width. This means the font-size changes depending on the string. I now use the following function, using jQuery:
function fontResize ( )
{
for (var i = $("#date").css("font-size").slice(0, -2); $("#date").width() < $("#clock").width(); i++)
$("#date").css("font-size", i.toString() + "px");
}
The idea is that I set the font-size in the CSS to the lowest possible value. I initialize the loop with this value and increment the font-size with 1 until the string is wider than the width of the containing element. The string in this case is "date" and the containing element is "clock".
Although this works, I think the main disadvantage is that the string has to be first drawn before the width can be determined. This means I cannot call this function before loading the body.
If anyone knows a better way to do this please let me know! Thanks.
To make sure you're getting all the styles and such applied to it that will be applied when the page is fully rendered, yes, you do want to put the element in the DOM (and in the right place in the DOM) before you do your measurement stuff. But you don't have to wait until everything else is there (unless you think it will affect the styling of the string you're measuring). You can put your code in a script block immediately after the element in question — no waiting for ready. The date element will be there and accessible, according to Google's Closure library engineers. E.g., if date is a span:
<body>
...
<span id="date">December 13th</span>
<script>fontResize();</script>
...
...
</body>
It's unfortunate to intermix code and markup like that (particularly if you have separate teams doing the markup and the code), but if your requirement is to size the text absolutely as soon as possible, that's how.
The above also assumes your fontResize function is already loaded (e.g., in a script block higher on the page). This is another reason it's unfortunate to mix markup and code like this, because normally of course you want to put your scripts at the bottom, just before closing the body tag.
However: It may be worth experimenting to see if you can do your resizing in the script you put just before the closing body tag instead. There'd be a small window of opportunity for the page not to look right, but quite small and of course pages tend to look a little funny as they load anyway. Then you wouldn't have the intermixing problem and wouldn't have to load your scripts early. You may find that the just-before-the-closing-body-tag is soon enough.
How about using the canvas, and the measureText method?
$(function () {
var canvas = $("canvas")[0];
var context = canvas.getContext("2d");
var text = "hello world";
context.font = "40pt Calibri";
var metrics = context.measureText(text);
alert(metrics.width);
});
I'm using the following JavaScript to dynamically load stylesheets:
function set_stylesheet(name) {
var link = document.getElementById('userstylelink');
link.href = link.href.replace(/[^\/]+\.css$/, name + '.css');
}
Is there any way to determine whether the new CSS file is loaded successfully? If it fails, I'd like to be able to apply a default stylesheet.
You might want to see my answer to another similar question here:
Detect and log when external JavaScript or CSS resources fail to load
Basically, you can add an onload callback to see if the file was loaded. (If you load via JS of course)
The simplest way is to check styleSheet.cssText property of the link element after a new href was assigned.
function set_stylesheet(name) {
var link = document.getElementById('userstylelink');
link.href = link.href.replace(/[^\/]+\.css$/, name + '.css');
if ( link.styleSheet.cssText ) {
//if not empty string, it was loaded
}
else {
link.href = "default.css";
}
Alternatively there is "onerror" event which fires when the resource fails to load after href assigned.
Ideally, you would load them all at the beginning and then switch between then.
http://www.alistapart.com/articles/alternate/
http://www.thesitewizard.com/javascripts/change-style-sheets.shtml
That doesn't really answer the question, but I think this way is preferred.
I'd recommend loading via ajax, checking the response as the user pimvbd mentions, but also place a dummy rule at the end of your stylesheet that styles a hidden element with a declaration you can check. For example, give a hidden div border: 987px and check to see if the hidden div's border is in fact 987px. Yes, it introduces a dependency on that style and that element. I've had endless discussions on this with many people, and there's not really a better way (yet). Hopefully s get some attention in browser releases in the near future...
There is a solution that requires no javascript or detection of whether the stylesheet loaded.
It seems you could also apply your default style with a built-in style sheet and then have the dynamically loaded stylesheet override the defaults. If the new stylesheet doesn't load, the default is already loaded and in place, nothing further to do. If the new stylesheet does load, it just overrides the default values and shows the new style.
Is there any technique to render a javascript file in the same time as the HTML is rendering?
My first idea was to load it into the head in a <script> tag, but as I see this doesn't affects the loading order, or I am false?
The problem is that in some times I need to use javascript to set an element's width when the page loads, and it's really annoying the little vibration what is because the javascript code which sets the elements width after the element was rendered in HTML.
Make the element render in HTML as invisible, and have the Javascript set the width then make it visible.
Unless you set the async flag (in some browsers) the JS file will block the loading of the content. So you can run your JS script from the moment it has loaded.
Might be better if you looked at a CSS solution though.
Support for async tags : Which browsers support <script async="async" />?
Yes you can with a tiny loader javascript like JcorsLoader (only 647B with Gzip)
JcorsLoader.load(
"http://xxxx/jquery.min.js",
function() {
$("#demo").html("jQuery Loaded");
},
"http://xxxx/jquery.cookie.js",
function() {
$.cookie('not_existing');
}
);
Load multiples js in parallel and execute in order without blocking DOMReady or onload.
https://github.com/pablomoretti/jcors-loader
You could use document.write in an inline script block to output the element's HTML, including an inline style. It will be ugly though; are you sure you can't avoid the whole thing?
Edit: a cleaner method is to document.write an inline style block that sets the element's width using an id selector before the element's HTML; this will degrade better but still avoids any potential problem of accessing the DOM before it's ready:
<script type="text/javascript">
var width = ...;
document.write('<style type="text/css">#myid{width:' + width + 'px;}</style>');
</script>
<div id="myid">...</div>