I'm making a website that has the option to load a "night mode" css stylesheet that they can toggle on and off. I'd like the style to persist as long as they're using the website. (It's a WordPress site.)
My lame attempt at the moment is to fill in the stylesheet with an IIFE. Obviously it's bad, and also I still get flash of unstyled content.
<link href="" id="night-css" rel="stylesheet">
<script>
(function () {
var style = localStorage.getItem('stylesheet');
if (style) {
document.getElementById('night-css').setAttribute('href', style);
}
})()
</script>
Would anyone have a suggestion for how to do this client-side?
Related
I have an app with the Vue CLI. When the app loads, I have a bunch of images that appear with a transition when a user clicks a button. The problem is that when the user clicks a button, the corresponding image only then starts to load, meaning that most of the animation is done until then. This makes the experience quite choppy because the images suddenly pop in during the transition, displacing other elements. I want to prefetch them when the site loads.
This answer suggests using the Image class. However, according to the Vue CLI docs, Vue internally uses its own plugin for that, preload-webpack-plugin, and it apparently can be configured.
I tried to configure it so that it preloads images:
vue.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('#vue/preload-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin({
rel: 'prefetch',
as (entry) {
if (/\.css$/.test(entry)) return 'style';
if (/\.woff$/.test(entry)) return 'font';
if (/\.png$/.test(entry)) return 'image';
return 'script';
}
})
]
}
}
This only manages to screw up the final index.html, leaving it without the build scripts and styles inside.
If I remove this line:
new HtmlWebpackPlugin(),
The site still loads but the images are not prefetched. It's the same as if I never did anything in the vue.config.js file.
How do I set it up correctly?
Edit: In Vue components, I use require() to load the images, meaning they pass through Webpack. For example:
<img :src="require('../assets/img/' + img)" draggable="false">
Edit: I was able to prefetch the images as Roy J suggested in the comments:
PreloadImages.vue in my main component:
<template>
<div style="display: none;">
<img :src="require('../assets/img/foo.png')">
<img :src="require('../assets/img/bar.png')">
<img :src="require('../assets/img/baz.png')">
</div>
</template>
However, that's not the answer to my actual question - it doesn't use resource hints via <link> tags. It also requires more effort and I believe it's a bad practice.
Since the plugin is already included by VueJS I think you have to modify it with chainWebpack.
According to the preload webpack plugin documentation, you should also set include option to 'allAssets' value.
It is very common in Webpack to use loaders such as file-loader to
generate assets for specific types, such as fonts or images. If you
wish to preload these files as well, you can use include with value
allAssets
So the configuration will be something like this:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('preload').tap(options => {
options[0].as = (entry) => {
if (/\.css$/.test(entry)) return 'style';
if (/\.woff$/.test(entry)) return 'font';
if (/\.png$/.test(entry)) return 'image';
return 'script';
}
options[0].include = 'allAssets'
// options[0].fileWhitelist: [/\.files/, /\.to/, /\.include/]
// options[0].fileBlacklist: [/\.files/, /\.to/, /\.exclude/]
return options
})
}
}
With a fresh Vue-cli installation I got the following HTML generated
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<meta http-equiv=X-UA-Compatible content="IE=edge">
<meta name=viewport content="width=device-width,initial-scale=1">
<link rel=icon href=/favicon.ico> <title>vue-preload-images</title>
<link href=/css/app.0b9b292a.css rel=preload as=style>
<!-- Here is the logo image -->
<link href=/img/logo.82b9c7a5.png rel=preload as=image>
<link href=/js/app.30d3ed79.js rel=preload as=script>
<link href=/js/chunk-vendors.3d4cd4b5.js rel=preload as=script>
<link href=/css/app.0b9b292a.css rel=stylesheet>
</head>
<body><noscript><strong>We're sorry but vue-preload-images doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript>
<div id=app></div>
<script src=/js/chunk-vendors.3d4cd4b5.js> </script> <script src=/js/app.30d3ed79.js> </script> </body> </html>
I hope it will work for you.
Solution 1.
When the user clicks the button you render your image in a non-visible state.
On image's load event you perform your transition smoothly.
If the loading of the image typically takes more than 2 seconds, consider giving the user a visual clue the button click was recorded and stuff is about to happen.
This would be the technically correct solution.
Solution 2.
An alternative, used extensively in production environments, (and having nothing to do with Vue per-se), is to load thumbnails of the images in your page (as in 10 times smaller - which is ~100 times smaller in size, being areas). It might not be obvious, but they make very good substitutes for large ones, while the transition is ongoing - you might also want to experiment with CSS blur filter on them.
The thumbnail and the large image have to be perfectly overlapped, in the same transitioning parent, with the large one underneath. On load event of the large version, fade the thumbnail out, causing a focus-in effect: a subtle eye-catcher.
A rather funny perk of this method is that, if you leave the thumbs on top (with opacity: 0), whenever someone tries to download an image they right click the thumb and have trouble understanding why they're downloading it at such a low res.
Everything is pretty much the same (in terms of animations) with the addition of a focus-in effect on the image, when it actually loaded.
Not DRY, but efficient. Looks professional, everything seems to load instantly and smoothly. Overall, it converts way more than other "correct" solutions. Which is why it's commonly used on websites where page performance is important.
When dealing with visuals, being perfect is looking perfect (and, in UX, feeling/seeming perfect).
Ancient greeks bent their columns so they looked straight when watched from below.
I'm trying to load a CSS dynamically in cordova over a xhr request.
The loading of the CSS is not a Problem, I can load it over xhr and store it to the filesystem over the HTML5 File API. Then I can get a URL this works perfectly.
But if i create a new link element in the header by javascript, like this:
<link rel="stylesheet" type="text/css" id="skin" href="cdvfile://localhost/temporary/mydomin.tdl/skin.css">
Thy stylesheet don't have any effect, how can I force cordova to take the stylesheet in account?
* UPDATE: I've got a working solution and I'll add it to my answer below *
I've found this problem and the suggested answers unfortunately haven't resolved it.
Loading the CSS data from an external PHP script via an XHR request (as my CSS data is dynamic to each page) I use:
var storeCSSURL = "https://www.example.com/dynamicCSS.php?storeID=x";
$('head').append('<link rel="stylesheet" href="' + storeCSSURL + '" type="text/css" />');
I'd also tried replacing the existing stylesheet link with the new URL; and added datetime stamp to it to prevent caching, which also didn't work.
Works great in the web browser and I know the data is loading through the XHR request and also being applied to the head CSS tag, although it doesn't work in Cordova / Phone Gap... the Apps just don't update with the CSS changes from the PHP script.
* NEW UPDATE *
I finally came up with a solution that works, it's a bit of a hack as it doesn't directly solve the problem; but works around it and is great for my needs.
In PhoneGap / Cordova, I use a pageInit.js type scenario that loads the web page in dynamically from a PHP script, I imagine most people use it in a somewhat similar way.
After page load I added:
$("body").append('<style id="dynamicStyles"></style>');
Then simply did a $.POST request to the Dynamic CSS (PHP) file, which returned all the dynamic style data; which I then loaded into a style tag.
This looks something like this:
$.post("https://www.example.com/controller.php", { url: url }, function (data, status) {
if (status == "success") {
$("body").html(data);
// Loads the main page content into the body tag
$("body").append('<style id="dynamicStyles"></style>');
// Appends the main page content with a style tag
$.post("https://www.example.com/dynamicCSS.php", { storeID: storeID }, function (data, status) {
if (status == "success") {
$("#dynamicStyles").html(data);
// Loads CSS data from external PHP script dynamically
// then places it into the new style tag.
}
});
}
});
The CSS updates from this line:
$("#dynamicStyles").html(data);
This loads all the new dynamic style data into the style tag; so the result is an on-page style definition, which you can replace the styles with using .html() at any stage from your external PHP with CSS data.
Phone Gap / Cordava recognises the style tag changes and updates visuals accordingly :-)
I'm sure you could set your project up to load all CSS data in this way instead of the normal head CSS link; and you'd never have that annoying CSS caching issue with Phone Gap / Cordova.
I hope this is useful to someone!
$('head').append('<link rel="stylesheet" href="style2.css" type="text/css" />');
$(document).ready(function () {
$("a").click(function () {
$('head').append('<link rel="stylesheet" href="style2.css" type="text/css" />');
});
});
When I launch my page, the css is totally messed up because my js is supposed to dynamically load css on click (mobile or standard website css). Currently, it just loads them both. Here's the code:
function loadjscssfile(filename, filetype)
{
if (filetype=="css")
{
var fileref = document.createElement("link");
fileref.rel= "stylesheet";
fileref.type = "text/css";
fileref.href = filename;
document.getElementsByTagName("head")[0].appendChild(fileref)
}
}
loadjscssfile("HCSS.css", "css")
I have two links on the site. One loads the mobile css, the other loads the standard website css. I have it linked like this:
load hcss
<br/>
load mobile
What you are after is swapping css files, not just loading a new one. In jquery it would probabaly look something like this (code not tested):
function swapCssFiles(fileToLoad, fileToUnload) {
$('head link[href="'+fileToUnload'"]') // select the tag with css to unload
.attr('href', fileToLoad); // swap the href attribute with the file to load
}
This is off course possible with 'pure' javascript, but I'm to much a jQuery addict to tell you how. If you see how easy the syntax is, you can probably tell why.
Your links would look something like this:
load hcss
I hope this is helpfull.
Note however that this is not the way I would approach this. If you want to target mobile devices with specific css, I would use mediaqueries to detect screensize, and not javascript.
I have a javascript that is loaded in the document head that needs to hide the document body until all page contents have loaded. I have tried:
$(document.body).hide();
But there still appears to be a flicker in some browsers. I am using the latest jQuery library (1.6.2). Firing the event on domready does hide the document body, but causing a flicker as well.
All that I can control is what is in the javascript. I cannot manipulate the static html pages as the script is being developed as a plugin.
Hiding content until the page is loaded is an anti-usability feature. Some parts of the content may take while to load, meanwhile your visitors see nothing. Browsers render content as it is received because users chose that as the preferred model in the very begining.
If you persist with this approach, you must hide the content using script. Otherwise, users with javascript disabled or not available, or where the script fails to execute correctly, will never see the content.
The simplest way to hide content using script is to use document.write to create a style sheet, then remove it to show the content:
document.write( '<style class="hideStuff" ' +
'type="text/css">body {display:none;}<\/style>');
window.onload = function() {
setTimeout(
function(){
var s, styles = document.getElementsByTagName('style');
var i = styles.length;
while (i--) {
s = styles[i];
if (s.className == 'hideStuff') {
s.parentNode.removeChild(s);
return;
}
}
}, 1000); // debug pause
}
The best way to do this is to put all content in a container div and have a style sheet that hides it by default. You can then show the content once everything is loaded. There is no way to run Javascript before the default page content renders so the only way to start out hidden is with a statically defined CSS rule:
HTML:
<body>
<div id="container">
all dynamic page content goes here
</div>
</body>
CSS in a stylesheet with the page to make it initially not visible:
#container {display: none;}
And, then you can do whatever you want with javascript and when you're done building the page, you do this to make it visible:
document.getElementById("container").style.display = "block";
or in jQuery:
$("#container").show();
You can use CSS and JS:
In the top of your document, below the TITLE use CSS:
<style type="text/css">body {visibility: hidden;}</style>
And after this use JS to restore the visibility:
<script type="text/JavaScript">
document.write('<style type="text/css">body {visibility: visible;}</style>');
</script>
Enjoy :)
I have the following file:
<html>
<head>
<title></title>
<link rel="css" type="text/css" href="/empty.css" title="css" />
<script type="text/javascript" src="/Prototype"></script>
<script type="text/javascript">
function load_content()
{
var d = new Date();
new Ajax.PeriodicalUpdater('content', '/DOC?'+d.getTime(),
{
method: 'post',
frequency: 5,
onSuccess: function(transport) {
for(i=0; (a = document.getElementsByTagName('link')[i]); i++)
{
if(a.getAttribute('rel') == 'css' && a.getAttribute("type") == 'text/css')
{
a.href = '/CSS?'+d.getTime();
}
}
}
});
}
</script>
</head>
<body>
<div id="content"></div>
<script type="text/javascript">
load_content();
</script>
</body>
</html>
Note: Ignore the d.getTime() calls...these are just to get around an issue with IE not loading a new page from an AJAX call because it's caching scheme is too aggressive.
Basically, when it reloads the file at /DOC, it is supposed to be setting the current stylesheet to the file at /CSS... both DOC and CSS and constantly changing.
What's weird is that in Chrome it works great. DOC loads up in the "content" div and the stylesheet gets set to CSS and that css is applied to the page. I can change with CSS page and withing 5 seconds, when the page is refreshed, the CSS will be refreshed as well.
But in IE and Firefox, the HTML will load and I can see that the href attribute of the stylesheet link IS getting changed to "/CSS + getTime()" but, while the HTML loads, the css is never applied to the page. I can even change the content of DOC and it updates, but the css is never even applied. It just stays a style-free page.
Does Firefox and IE not support changing the style sheet reference in this way?
Is there a better way to do this?
Rather than changing the sheet in a single link, try using alternate style sheets. See this link on using alternate style sheets:
http://www.alistapart.com/articles/alternate/
The best way to include files via javascript is to insert a new dom element.
var a = document.createElement('link');
a.href="inset.css";
a.rel="stylesheet";
document.getElementsByTagName("head")[0].appendChild(a);
However, obviously the problem you're going to run into though is that firefox and ie will not repaint the canvas once the document is finished loading (and you're using ajax). The way you get around that is by taking the contents of the stylesheets and including them in a style element. This sample code will change the color on the page dynamically.
function onLoadFunction() {
var a = document.createElement('style');
a.appendChild(document.createTextNode('body {color: blue;}'));
document.getElementsByTagName("body")[0].appendChild(a);
}
When you load a new sheet, just destroy the css inside the style element and replace it.
maybe this will help you ...
http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
function loadjscssfile(filename, filetype){
if (filetype=="js"){ //if filename is a external JavaScript file
var fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", filename)
}
else if (filetype=="css"){ //if filename is an external CSS file
var fileref=document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("href", filename)
}
if (typeof fileref!="undefined")
document.getElementsByTagName("head")[0].appendChild(fileref)
}
It looks like you are simply reloading the existing page every time. Why not just use the refresh tag in your header to force the document to reload each time and put in the CSS and content server-side. A lot simpler and it works even with javascript disabled.
<meta http-equiv="refresh" content="5;url=http://example.com/DOC" />
It might be a caching issue. If you do a hard refresh (Ctrl+R in FF, Ctrl+F5 in IE) does it display the style properly? If that does fix it, you may want to look at removing the Last-Modified header from the CSS file or adding a cache control header telling the browser not to cache it.