How to determine if CSS has been loaded? - css

How can i Assert that the CSS for a page has successfully loaded and applied its styles in Watin 2.1?

After doing some research and writing up my answer, I stumbled upon this link that explains everything you need to know about CSS, when it is loaded and how you can check for it.
The link provided explains it so well, in fact, that I'm adding some quotes from it for future reference.
If you're curious, my answer was going to be #2 and a variation of #4.
When is a stylesheet really loaded?
...
With that out of the way, let's see what we have here.
// my callback function
// which relies on CSS being loaded function
CSSDone() {
alert('zOMG, CSS is done');
};
// load me some stylesheet
var url = "http://tools.w3clubs.com/pagr/1.sleep-1.css",
head = document.getElementsByTagName('head')[0],
link = document.createElement('link');
link.type = "text/css";
link.rel = "stylesheet";
link.href = url;
// MAGIC
// call CSSDone() when CSS arrives
head.appendChild(link);
Options for the magic part, sorted from nice-and-easy to ridiculous
listen to link.onload
listen to link.addEventListener('load')
listen to link.onreadystatechange
setTimeout and check for changes in document.styleSheets
setTimeout and check for changes in the styling of a specific element you create but style with the new CSS
5th option is too crazy and assumes you have control over the content of the CSS, so forget it. Plus it checks for current styles in a timeout meaning it will flush the reflow queue and can be potentially slow. The slower the CSS to arrive, the more reflows. So, really, forget it.
So how about implementing the magic?
// MAGIC
// #1
link.onload = function () {
CSSDone('onload listener');
};
// #2
if (link.addEventListener) {
link.addEventListener('load', function() {
CSSDone("DOM's load event");
}, false);
};
// #3
link.onreadystatechange = function() {
var state = link.readyState;
if (state === 'loaded' || state === 'complete') {
link.onreadystatechange = null;
CSSDone("onreadystatechange");
}
};
// #4
var cssnum = document.styleSheets.length;
var ti = setInterval(function() {
if (document.styleSheets.length > cssnum) {
// needs more work when you load a bunch of CSS files quickly
// e.g. loop from cssnum to the new length, looking
// for the document.styleSheets[n].href === url
// ...
// FF changes the length prematurely :(
CSSDone('listening to styleSheets.length change');
clearInterval(ti);
}
}, 10);
// MAGIC ends

There has been an update to the article lined to by #ShadowScripter. The new method purportedly works in all browsers, including FF.
var style = document.createElement('style');
style.textContent = '#import "' + url + '"';
var fi = setInterval(function() {
try {
style.sheet.cssRules; // <--- MAGIC: only populated when file is loaded
CSSDone('listening to #import-ed cssRules');
clearInterval(fi);
} catch (e){}
}, 10);
document.getElementsByTagName('head')[0].appendChild(style);

After page load you can verify the style on some of your elements something like this:
var style = browser.Div(Find.ByClass("class")).Style;
Assert.That(Style.Display, Is.StringContaining("none"));
Assert.That(Style.FontSize, Is.EqualTo("10px"));
And etc...

Since browser compatibility can vary, and new future browser standards subject to change, I would recommend a combination of the onload listener and adding CSS to the style sheet so you can listen for when the HTML elements z-index changes if you are using a single style sheet. Otherwise, use the function below with a new meta tag for each style.
Add the following to the CSS file that you are loading:
#*(insert a unique id for he current link tag)* {
z-index: 0
}
Add the following to your script:
function whencsslinkloads(csslink, whenload ){
var intervalID = setInterval(
function(){
if (getComputedStyle(csslink).zIndex !== '0') return;
clearInterval(intervalID);
csslink.onload = null;
whenload();
},
125 // check for if it has loaded 8 times a second
);
csslink.onload = function(){
clearInterval(intervalID);
csslink.onload = null;
whenload();
}
}
Example
index.html:
<!doctype html>
<html>
<head>
<link rel=stylesheet id="EpicStyleID" href="the_style.css" />
<script async href="script.js"></script>
</head>
<body>
CSS Loaded: <span id=result>no</span>
</body>
</html>
script.js:
function whencsslinkloads(csslink, whenload ){
var intervalID = setInterval(
function(){
if (getComputedStyle(csslink).zIndex !== '0') return;
clearInterval(intervalID);
csslink.onload = null;
whenload();
},
125 // check for if it has loaded 8 times a second
);
csslink.onload = function(){
clearInterval(intervalID);
csslink.onload = null;
whenload();
}
}
/*************************************/
whencsslinkloads(
document.getElementById('EpicStyleID'),
function(){
document.getElementById('result').innerHTML = '<font color=green></font>'
}
)
the_style.css
#EpicStyleID {
z-index: 0
}
PLEASE do not make your script load synchronously (without the async attribute) just so you can capture the link's onload event. There are better ways, like the method above.

Related

Linking to specific ID on another Wordpress page?

I have a header.php, that appears in every single page on my blog, with a navbar that looks like this:
<nav>
<ul>
<li>Logistics</li>
<li>Contact</li>
</ul>
</nav>
But when I click the anchor tag linking to #contact, which is located in page with id 5, as you can see by the php code, nothing happens. I tried using a slash (/#contact) but I keep getting the same behavior. Isn't this the correct way of linking to a specific id on another page?
EDIT: I also have some smooth scrolling code (below) which I think may be related to my issue.
<script>
$( document ).ready( function () {
// Add smooth scrolling to all links
$( "a" ).on( 'click', function ( event ) {
// Make sure this.hash has a value before overriding default behavior
if ( this.hash !== "" ) {
// Prevent default anchor click behavior
event.preventDefault();
// Store hash
var hash = this.hash;
// Using jQuery's animate() method to add smooth page scroll
// The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
$( 'html, body' ).animate( {
scrollTop: $( hash ).offset().top
}, 800, function () {
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
} );
} // End if
} );
} );
</script>
Thanks!
You have a javascript error.
<script>
$( document ).ready( function () {
// Add smooth scrolling to all links
$( "a" ).on( 'click', function ( event ) {
// Make sure this.hash has a value before overriding default behavior
if ( this.hash !== "" ) {
// Prevent default anchor click behavior
event.preventDefault();
// Store hash
var hash = this.hash;
// Using jQuery's animate() method to add smooth page scroll
// The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
$( 'html, body' ).animate( {
scrollTop: $( hash ).offset().top
}, 800, function () {
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
} );
} // End if
} );
} );
</script>
Here is the problem. On the home page you have a div with id #contato. On the other pages it is not there.
$( 'html, body' ).animate( {
scrollTop: $( hash ).offset().top
}, 800, function () {
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
} );
Your hash var's value is "#contato", but when js tries to get it's offset you recieve an error saying that it's impossible to read property top of 'undefined'. So, you can try to remove that part of the script from the other pages and only keep it on home. This should work like a charm. If you have any question, please ask.
LE: I'd recommend this method for smooth scroll (I didn't tested it for your case, but it always worked for me)
$(function() {
$('a[href*="#"]:not([href="#"])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html, body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
});
});
use
<?php echo get_page_link(5)."#contact"; ?>
instead of get permalink function

Load multiple pages in a hidden iframe from a xul-based firefox extension

From a xul-based firefox addon, I need to:
programmatically create an invisible iframe (once)
reuse it to load multiple URLs as the addon runs
access the returned HTML after each URL loads
Problem: I can only get the first page-load for any created iframe to trigger an 'onload' or 'DOMContentLoaded' event. For subsequent URLs, there is no event triggered.
Note: I'm also fine with using the hiddenDOMWindow itself if this is possible...
Code:
var urls = ['http://en.wikipedia.org/wiki/Internet', 'http://en.wikipedia.org/wiki/IPv4', 'http://en.wikipedia.org/wiki/Multicast' ];
visitPage(urls.pop());
function visitPage(url) {
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var hiddenWindow = Components.classes["#mozilla.org/appshell/appShellService;1"].getService
(Components.interfaces.nsIAppShellService).hiddenDOMWindow;
var doc = hiddenWindow.document, iframe = doc.getElementById("my-iframe");
if (!iframe)
{
iframe = doc.createElement("iframe");
//OR: iframe = doc.createElementNS(XUL_NS,"iframe");
iframe.setAttribute("id", "my-iframe");
iframe.setAttribute('style', 'display: none');
iframe.addEventListener("DOMContentLoaded", function (e) {
dump('DOMContentLoaded: '+e.originalTarget.location.href);
visitPage(urls.pop());
});
doc.documentElement.appendChild(iframe);
}
iframe.src = url;
}
There are some traps:
The hiddenWindow differs between platforms. It is XUL on Mac, and HTML else.
You should use .setAttribute("src", url); to reliably navigate.
The following works for me (Mac, Win7):
var urls = [
'http://en.wikipedia.org/wiki/Internet',
'http://en.wikipedia.org/wiki/IPv4',
'http://en.wikipedia.org/wiki/Multicast'
];
var hiddenWindow = Components.classes["#mozilla.org/appshell/appShellService;1"].
getService(Components.interfaces.nsIAppShellService).
hiddenDOMWindow;
function visitPage(url) {
var iframe = hiddenWindow.document.getElementById("my-iframe");
if (!iframe) {
// Always use html. The hidden window might be XUL (Mac)
// or just html (other platforms).
iframe = hiddenWindow.document.
createElementNS("http://www.w3.org/1999/xhtml", "iframe");
iframe.setAttribute("id", "my-iframe");
iframe.addEventListener("DOMContentLoaded", function (e) {
console.log("DOMContentLoaded: " +
e.originalTarget.location);
var u = urls.pop();
// Make sure there actually was something left to load.
if (u) {
visitPage(u);
}
});
hiddenWindow.document.documentElement.appendChild(iframe);
}
// Use .setAttribute() to reliably navigate the iframe.
iframe.setAttribute("src", url);
}
visitPage(urls.pop());
Don't reload the hiddenWindow itself, or you will break lots of other code.

Jquery Script Tag in Header

I know this is a really basic question, so forgive me. I have a script that works in a jfiddle, but I want to put it in my header and I can't figure out how to call it with a script tag and event handler(?).
Here's the script:
var retrieveValue = function(ev){
var $this = $(this),
val = $this.data('value');
if (val) {
$this.val(val);
}
},
hideValue = function(ev){
var $this = $(this);
$this.data('value', $this.val());
$this.val($this.val().replace(/^\d{5}/, '*****'));
};
$('#field_a7afui').focus(retrieveValue);
$('#field_a7afui').blur(hideValue);
$('#form_hv3hcs').submit(function(ev){
ev.preventDefault();
retrieveValue.call($('#field_a7afui')[0], ev);
alert($('#field_a7afui').val());
hideValue.call($('#field_a7afui')[0], ev);
});
Can someone please tell me what I need to put at the beginning and end of this just to throw it in my Wordpress header and call it a day?
Here's my jfiddle: http://jsfiddle.net/d5KaJ/40/
If that's what you were asking for...
into a script tag like:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script>
jQuery(function( $ ) {
// CODE HERE
} )();
</script>

Reading documents CSS in Chrome Extension

I am trying to read the pages CSS using a chrome extension. This is what i have in my content script :
var allSheets = document.styleSheets;
for (var i = 0; i < allSheets.length; ++i) {
var sheet = allSheets[i];
var src = sheet.href;
var rules = sheet.cssRules || sheet.rules;
}
For some reason the rules are always empty. I do get all the CSS files used in the 'src' variable. But the rules always come as null.. Its working when I try it as a separate javascript on a HTML page. But fails when I put it up in the content script of my chrome extension. Can somebody lemme know why?
Well thats the Why, but for fun and interest (never done anything with style sheets before) I thought Id do a How....
manifest.json
{
"name": "Get all css rules in stylesheets",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js" : ["myscript.js"],
"run_at":"document_end"
}
],
"permissions": [
"tabs", "<all_urls>"
],
"version":"1.0"
}
myscript.js
// Create the div we use for communication
var comDiv = document.createElement('div');
comDiv.setAttribute("id", "myCustomEventDiv");
document.body.appendChild(comDiv);
// Utitlity function to insert some js into the page, execute it and then remove it
function exec(fn) {
var script = document.createElement('script');
script.setAttribute("type", "application/javascript");
script.textContent = '(' + fn + ')();';
document.body.appendChild(script); // run the script
document.body.removeChild(script); // clean up
}
// function that gets inserted into the page
// iterates through all style sheets and collects their rules
// then sticks them in the comDiv and dispatchs the event that the content script listens for
getCSS=function (){
var rules = '';
// Create the event that the content script listens for
var customEvent = document.createEvent('Event');
customEvent.initEvent('myCustomEvent', true, true);
var hiddenDiv = document.getElementById('myCustomEventDiv');
var rules ='';
var allSheets = document.styleSheets;
for (var i = 0; i < allSheets.length; ++i) {
var sheet = allSheets[i];
for (var z = 0; z <= sheet.cssRules.length-1; z++) {
rules = rules +'\n'+ sheet.cssRules[z].cssText;
}
}
hiddenDiv.innerText = rules;
hiddenDiv.dispatchEvent(customEvent);
}
// puts the rules back in the page in a style sheet that the content script can iterate through
// youd probably do most of this in the injected script normally and pass your results back through the comDiv....Im just having fun
document.getElementById('myCustomEventDiv').addEventListener('myCustomEvent', function() {
var eventData = document.getElementById('myCustomEventDiv').innerText;
document.getElementById('myCustomEventDiv').innerText='';
var style = document.createElement('style');
style.type = 'text/css';
style.innerText=eventData;
style = document.head.appendChild(style);
var sheet = document.styleSheets[document.styleSheets.length-1];
for (var z = 0; z <= sheet.cssRules.length-1; z++) {
console.log(sheet.cssRules[z].selectorText +' {\n');
for (var y = 0; y <= sheet.cssRules[z].style.length-1; y++) {
console.log(' '+sheet.cssRules[z].style[y] + ' : ' + sheet.cssRules[z].style.getPropertyValue(sheet.cssRules[z].style[y])+';\n');
};
console.log('}\n');
};
// Clean up
document.head.removeChild(style);
document.body.removeChild(document.getElementById('myCustomEventDiv'));
});
exec(getCSS);
In the case of this question Id prolly do most of the checks in the injected script and then pass the results back through the div and its event. But I wanted to see if I could use the dom methods in the content script to go through the css and this was the only way I could figure to do it. I dont like the idea of inserting the rules back into the page, but couldnt figure any other way of doing it.
Just a guess, but since chrome extensions are Javascript based, they may have cross domain issues. Chrome sets the rules and cssRules to null when programmatically trying to get a stylesheet from another domain.
For getting all external css and all internal css file, you can use devtools API. If you want to use it in chrome extension you need to hook devtool into you chrome extension. This code will work
chrome.devtools.panels.create(
'my chrome extension',
'icon.png',
'index.html',
function(panel) {
var initial_resources = {};
// collect our current resources
chrome.devtools.inspectedWindow.getResources(function(resources) {
for (var i = 0, c = resources.length; i < c; i++) {
if (resources[i].type == 'stylesheet') {
// use a self invoking function here to make sure the correct
// instance of `resource` is used in the callback
(function(resource) {
resource.getContent(function(content, encoding) {
initial_resources[resource.url] = content;
});
})(resources[i]);
}
}
});
}
);
Answer is late, but I think I can help. One method of accessing the cssRules of external sheets protected by CORs is to use Yahoo's YQL service. I've incorporated it into a developer tools extension for Chrome for capturing styles and markup for a page fragment. The extension is in the Chrome Web Store and is on Github.
Grab the source from Github and look at the content.js script to see how YQL is used. Basically, you'll make an AJAX call to YQL and it will fetch the CSS for you. You'll need to take the CSS content and either inject it into the page as an embedded style tag or parse the CSS using JavaScript (there are some libraries for that purpose). If you choose to inject them back into the document, make sure to set the new style blocks to disabled so that you don't screw up the rendering of the page.
The extension itself might be useful to you:

IE9 not rendering iframe in ASP.NET application

I have a parent page which has an iframe and also has javascript which will create a form, append it to the iframe, and submits it via POST to an external URL upon page load.
The content from the external URL then loads in the iframe. This works fine in all browsers EXCEPT IE9.
I tried the 'meta http-equiv="X-UA-Compatible" content="IE=8" ' trick and this didn't help. Sometimes the iframe renders the content, sometimes it doesn't upon refresh. Debug statements in the javascript show it is firing each time (each page load) and Fiddler shows the successful request/response to the external URL. It's as if IE9 selectively decides whether to update the DOM.
Also I've noticed is that if there is any sort of delay with the external request (taking a few seconds), then the iframe content never renders. Has anyone experienced this with IE9 and have a solution?
<iframe frameborder="0" height="600px" id="ifPage" runat="server" width="700px" />
<script type="text/javascript" language="javascript">
var alreadyrunflag = 0 //flag to indicate whether target function has already been run
if (document.addEventListener) {//FireFox or Sarafi
document.addEventListener("DOMContentLoaded", function () { alreadyrunflag = 1; GetExternalPageContent() }, false)
}
else if (document.all && !window.opera)
{//IE
addLoadEvent(GetExternalPageContent)
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
}
else {
window.onload = function () {
if (oldonload) {
oldonload();
}
func();
}
}
}
function GetExternalPageContent() {
var iframe = document.getElementsByTagName("iframe");
if (iframe != null) {
var uniqueString = "embFrame";
iframe[0].contentWindow.name = uniqueString;
var form = document.createElement("form");
form.target = uniqueString;
form.action = '<%=ExternalUrl %>';
form.method = "POST";
//parameter submitted to external URL to get appropriate content
var input = document.createElement("input");
input.type = "hidden";
input.name = "embParam";
input.value = "paramValue1";
form.appendChild(input);
document.body.appendChild(form);
form.submit();
}
}
</script>
I just wanted to let people know that the issue here is that IE doesn't like naming of the iframe content window like this:
iframe[0].contentWindow.name = uniqueString
Instead, the name attribute must be within the iframe tag itself. There were no javascript errors indicating this, it just didn't consistently render. Then, when you need to dynamically reference the iframe content, use:
var iframe = window.frames['embFrame']
Doing it this way solved the issue and now the iframe content is rendered consistently.

Resources