Saving dark mode after refresh - javascript

I have some code that saves my dark theme after refresh:
(thanks to https://stackoverflow.com/users/519413/rory-mccrossan on this post: Day/Night Toggle Using Cookie to Save on Page Refresh )
jQuery(document).ready(function($) {
$(".theme__switch").on("click", () => {
$(".theme__switch").toggleClass("active");
$("body").toggleClass("dark__theme");
$.cookie("toggle", $(".theme__switch").hasClass('active'));
});
if ($.cookie("toggle") === "true") {
$(".theme__switch").addClass("active");
$("body").addClass("dark__theme");
}
});
The only issue I find with this solution is that it flashes the original state before adding active to the toggle. So it flashes the original white background before adding the dark theme class. Is there a solution to avoid the flicker? or is this as good as it gets

The reason is that your code under jQuery(document).ready function runs when the page fully loaded. So, you have a delay to see its result and see the flashing.
There is no choice in framework-less websites except adding a loading frame fit to the total page at first and add a code to remove it after jQuery comes ready.
Assume this for example:
html
<div id="overlay">
<!-- some loading text or elements -->
</div>
css
#overlay {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: #fff;
}
js
jQuery(document).ready(function($) {
$(".theme__switch").on("click", () => {
$(".theme__switch").toggleClass("active");
$("body").toggleClass("dark__theme");
$.cookie("toggle", $(".theme__switch").hasClass('active'));
});
if ($.cookie("toggle") === "true") {
$(".theme__switch").addClass("active");
$("body").addClass("dark__theme");
}
jQuery('#overlay').hide();
});

Related

Add class after delay with vanilla javascript

I am making a website with a splash screen that I want to make disappear after 3 seconds. I can successfully do it when I include jQuery, but this takes time to load (especially if it's not cached) and so the splash still displays for a small time.
I am also using cookies so that it will only show on the first load of the page (so it's not overly irritating).
Here's my HTML:
<div class="splash">
splash content
</div>
Here's the working jQuery (that I want to avoid):
if(document.cookie.indexOf("visited=true") === -1) {
$(".splash").delay(3000).queue(function(){
$(this).addClass("hidden").dequeue();
});
} else {
$(".splash").addClass("hidden");
}
Here's what I have come up with regarding javascript, but it doesn't work:
document.getElementsByClassName("splash").addEventListener("load",
function() {
if(document.cookie.indexOf("visited=true") === -1) {
setTimeout(function(){
this.classList.add("hidden");
}, 3000);
} else {
this.classList.add("hidden");
}
});
I don't think you want to add the function as the load event listener of the splash. You should add it to the load event of the page.
See comments inline for more details on reorganizing the code. Unfortunately, it won't work with cookies here in the Stack Overflow snippet environment.
Note that the splash is set to be hidden (via CSS) by default. This is a better practice than showing it by default and then hiding it. If, after reading the cookie, it is determined that the splash should not be shown, some users may wind up seeing the splash momentarily on their screens due to processing limitations, or worse if there is any kind of error in your code, the splash may wind up being shown and never taken away because the JS stops executing at the error.
// Get a reference to the splash dialog
var splash = document.querySelector(".splash");
// When the window is loaded....
window.addEventListener("load", function() {
// Check to see if the cookie indicates a first-time visit
if(document.cookie.indexOf("visited=true") === -1) {
// Reveal the splash (remember: splash is hidden by default by CSS)
splash.classList.remove("hidden");
// .5 seconds later, hide the splash
setTimeout(function(){
splash.classList.add("hidden");
// >> Set cookie to visited here <<
}, 500);
}
});
.splash {
height:200px;
width:200px;
background:yellow;
}
.hidden {
display:none;
}
<div class="splash hidden">S P L A S H !</div>
document.getElementsByClassName("splash").addEventListener("load", //not possible as ByClassName returns a collection not an element
function() {
if(document.cookie.indexOf("visited=true") === -1) {//why not simply !...
setTimeout(function(){
this.classList.add("hidden");//this is window as setTimeout is a window function...
}, 3000);
} else {
this.classList.add("hidden");//the only that work
}
});
The right way:
document.getElementsByClassName("splash").forEach(el=>{el.addEventListener("load",function() {
if(!document.cookie.indexOf("visited=true")) {
setTimeout(function(){
this.classList.add("hidden");
}.bind(this), 3000);//fix of context
} else {
this.classList.add("hidden");
}
})});
You can include this IIFE at the bottom of your page so that it will be executed when the splash DOM element is ready. This way you can avoid the event listener.
I also converted your splash to use the ID splash rather than a class. If you prefer the class, when you use document.getElementsByClassName("splash") it returns an array of elements. In that case you'll have to specify which elements of the returned array you want to use (i.e. document.getElementsByClassName("splash")[0] or iterate through them).
(function() {
var splash = document.getElementById("splash");
if (document.cookie.indexOf("visited=true") === -1) {
splash.classList.remove("hidden"); // Display the splash
setTimeout(function() {
splash.classList.add("hidden"); // Hide it after the timeout
}, 500);
}
})();
#splash { position: absolute; left: 0; right: 0; top: 0; bottom: 0; background: #ddd; }
.hidden { display: none; }
Not splashed!
<div id="splash" class="hidden">Splashed!</div>

Apply jQuery to body (background) but not Modal using Bootstrap

I am trying to apply foggy.js (http://nbartlomiej.github.io/foggy/) to the back-drop of Bootstraps Modal. This would allow me to blur every element in the background which I have completed so far but the Modal Popup is also Blurry. So my question is how do I apply the blur to the back-drop and its child elements so the Modal isn't blurred but everything else is.
Here is my code. Note I have some code to start and stop Vimeo as well as start and stop the Carousel.
$(document).ready(function(){
$('.close-video').on('click', function (event) {
for (var i = 0; i < window.frames.length; i++) {
window.frames[i].postMessage(JSON.stringify({ method: 'pause' }), '*') //Pauses Vimeo Video
$('#AVRS-Carousel').carousel('cycle'); //Starts Carousel
$('body').foggy(false); //Disables Foggy.js
}
});
$('[data-frame-index]').on('click', function (event) {
var $span = $(event.currentTarget),
index = $span.data('frame-index');
window.frames[index].postMessage(JSON.stringify({ method: 'play' }), '*') //Auto Plays Vimeo
$('#AVRS-Carousel').carousel('pause'); //Pauses Carousel
$('body').foggy(); //Applies Foggy.js
});
});
Also here is CSS for Bootstraps back-drop class for the Modal:
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: #zindex-modal-background;
background-color: #modal-backdrop-bg;
// Fade for backdrop
&.fade { .opacity(0); }
&.in { .opacity(#modal-backdrop-opacity); }
filter: blur(5px)
}
Thank you for your time in advance.
I have found no way to remove the blur effect from child elements. In the end I applied the blur to nav and section elements and kept modals outside of them. Bootstraps documentation for modals also warns about markup placement, saying:
Always try to place a modal's HTML code in a top-level position in your document to avoid other components affecting the modal's appearance and/or functionality.
So the HTML ends up looking something like this:
<body>
<nav>...</nav>
<div class="modal">...</div>
<section>...</section>
</body>
Javascript (using bootstraps data-toggle attribute on a button):
$('button[data-toggle]').on('click', function (event) {
$('nav, section').foggy();
});
I hope this was somewhat helpful.

Getting a spinner working properly in Grails

I'm trying to get a spinner working properly in my grails app. The way I understand it, is it should work out of the box while waiting for an action to complete. It is not doing this.
I was able to get a spinner working based on some suggestions I found from google, and modified this solution: http://grails.1312388.n4.nabble.com/spinner-or-progress-indicator-td1363802.html , however this seems rather hacky to me, and not an optimal solution.
Which indicated I needed the following script:
<script type="text/javascript">
function showSpinner() {
document.getElementById('spinner').style.display = 'inline';
document.getElementById('error').style.display = 'none';
}
function hideSpinner() {
document.getElementById('spinner').style.display = 'none';
document.getElementById('error').style.display = 'none';
}
function showError(e) {
var errorDiv = document.getElementById('error')
errorDiv.innerHTML = '<ul><li>'
+ e.responseText + '</li></ul>';
errorDiv.style.display = 'block';
}
</script>
Which I employed with a grails button as such:
<g:submitToRemote value="Validate Address"
url="[action: 'standardizedList', controller: 'address']"
update="addressList" after="showSpinner();" onSuccess="hideSpinner()"
class="btn btn-primary"/><br>
<img id="spinner" style="display:none;"
src="${createLinkTo(dir: 'images', file: 'spinner.gif')}"
alt="Spinner"/>
Now I put the javascript snippet into /layouts/main.gsp, but it appears I have to add the spinner image into each page where I want it, and if I put it in the div where I want it to show, it will be overwritten when the action completes and updates the div so I have to add the spinner in the page that is responding as well as inside the response body.
When I look at the given main.gsp it has the following code in it:
<div id="spinner" class="spinner" style="display:none;">
<g:message code="spinner.alt" default="Loading…"/>
</div>
furthermore, found inside the web-app/js/ directory there is a file application.js which contains the code that I saw frequently which is supposed to add the spinner.
if (typeof jQuery !== 'undefined') {
(function($) {
$('#spinner').ajaxStart(function() {
$(this).fadeIn();
}).ajaxStop(function() {
$(this).fadeOut();
});
})(jQuery);
}
Now I have several places where I believe actions may cause a delay, and I want the spinner to tell the user it's working. So my question is two fold: 1) Am I understanding how it is supposed to be working? If so, 2) how can I make the out of the box code work properly?
Here's how I do it: make sure the following JavaScript is included in every page, e.g. by putting it in a .js file which is included in the layout:
$(document).ready(function () {
var showSpinner = function() {
$("#spinner").fadeIn('fast');
};
// Global handlers for AJAX events
$(document)
.on("ajaxSend", showSpinner)
.on("ajaxStop", function() {
$("#spinner").fadeOut('fast');
})
.on("ajaxError", function(event, jqxhr, settings, exception) {
$("#spinner").hide();
});
});
The functions above will be called every time an AJAX request is stopped, started, or returns with an error. Also include the following in the layout:
<div id="spinner" style="display:none;">
<g:img uri="/images/spinner.gif" alt="Loading..."/>
</div>
This is the content which is shown/hidden when an AJAX request starts/stops. In my case I apply the following styles, so that the spinner is shown in the center of the screen on top of any other content:
#spinner {
position: fixed;
top: 50%;
left: 50%;
margin-left: -50px; // half width of the spinner gif
margin-top: -50px; // half height of the spinner gif
z-index: 5000;
overflow: auto;
}

One div visible and other hidden

I am no expert on programing with jQuery but I have a little bit knowledge about the language, the thing is that I want one div to be visible and the other one hidden, as soon as you click the other div it should slide down and the first one should be hidden.
The bug is that if you press one div atm is messes up.
$(document).ready(function () {
$('#link').click(function () {
if ($('.todo-post').is(":hidden")) {
$('#date-visible').slideUp("slow");
$('#date-hidden').slideDown("slow");
$('#tick-hidden').slideDown("slow");
$('.todo-post').slideDown("slow");
} else {
$('.todo-post').slideUp("slow");
$('#date-hidden').slideUp("slow");
$('#tick-hidden').slideUp("slow");
$('#date-visible').slideDown("slow");
}
});
});
That's the code I'm using at the moment, It works for one div there is text everywhere if I add another div, it gets messy. I believe that the code can be re-made so it works properly but sadly I do not know how and I have been searching the web for a while now.
LINK TO MY WEBSITE
You can do this with less code
$(document).ready(function () {
$('#link').on('click', function () {
$('#date-visible, #date-hidden, #tick-hidden, .todo-post').slideToggle("slow");
});
});
Basically what is happening is that our elements have position absolute so if you add this css it will work:
div.todo-avatar-date-hidden {
position: static;
}
div.todo-tick {
position: static;
}
div.todo-post {
position: static;
}
Also you need to put it relatively near the bottom of your css or it will be overridden by the previous code so I advise to go to each element in the css that I have shown and removing the line that makes the element absolute
Edit
$('#link').click(function () {
if($('#date-visible').is(':hidden')) {
if(!($('#date-visible-2').is(':hidden'))) {
$('#date-visible-2, #date-hidden-2, #tick-hidden-2, .todo-post-2').slideToggle("slow");
}
}
$('#date-visible, #date-hidden, #tick-hidden, .todo-post').slideToggle("slow");
});
$('#link-2').on('click', function () {
if($('#date-visible-2').is(':hidden')) {
if(!($('#date-visible').is(':hidden'))) {
$('#date-visible, #date-hidden, #tick-hidden, .todo-post').slideToggle("slow");
}
}
$('#date-visible-2, #date-hidden-2, #tick-hidden-2, .todo-post-2').slideToggle("slow");
});
IDs should be unique, no two elements can have the same id in a same page. You are using same id like "date-visible" in your HTML page. Change them and then code accordingly.

Store positioning information from each element (JQuery/Javascript)

Pleasantries
I've been playing around with this idea for a couple of days but can't seem to get a good grasp of it. I feel I'm almost there, but could use some help. I'm probably going to slap myself right in the head when I get an answer.
Actual Problem
I have a series of <articles> in my <section>, they are generated with php (and TWIG). The <article> tags have an image and a paragraph within them. On the page, only the image is visible. Once the user clicks on the image, the article expands horizontally and the paragraph is revealed. The article also animates left, thus taking up the entire width of the section and leaving all other articles hidden behind it.
I have accomplished this portion of the effect without problem. The real issue is getting the article back to where it originally was. Within the article is a "Close" <button>. Once the button is clicked, the effect needs to be reversed (ie. The article returns to original size, only showing the image, and returns to its original position.)
Current Theory
I think I need to retrieve the offset().left information from each article per section, and make sure it's associated with its respective article, so that the article knows where to go once the "Close" button is clicked. I'm of course open to different interpretations.
I've been trying to use the $.each, each(), $.map, map() and toArray() functions to know avail.
Actual Code
/*CSS*/
section > article.window {
width:170px;
height:200px;
padding:0;
margin:4px 0 0 4px;
position:relative;
float:left;
overflow:hidden;
}
section > article.window:nth-child(1) {margin-left:0;}
<!--HTML-->
<article class="window">
<img alt="Title-1" />
<p><!-- I'm a paragraph filled with text --></p>
<button class="sClose">Close</button>
</article>
<article class="window">
<!-- Ditto + 2 more -->
</article>
Failed Attempt Example
function winSlide() {
var aO = $(this).parent().offset()
var aOL = aO.left
var dO = $(this).offset()
var dOL = dO.left
var dOT = dO.top
var adTravel = dOL-aOL
$(this).addClass('windowOP');
$(this).children('div').animate({left:-(adTravel-3)+'px', width:'740px'},250)
$(this).children('div').append('<button class="sClose">Close</button>');
$(this).unbind('click', winSlide);
}
$('.window').on('click', winSlide)
$('.window').on('click', 'button.sClose', function() {
var wW = $(this).parents('.window').width()
var aO = $(this).parents('section').offset()
var aOL = aO.left
var pOL = $(this).parents('.window').offset().left
var apTravel = pOL - aOL
$(this).parent('div').animate({left:'+='+apTravel+'px'},250).delay(250, function() {$(this).animate({width:wW+'px'},250); $('.window').removeClass('windowOP');})
$('.window').bind('click', winSlide)
})
Before you go scratching your head, I have to make a note that this attempt involved an extra div within the article. The idea was to have the article's overflow set to visible (.addclass('windowOP')) with the div moving around freely. This method actually did work... almost. The animation would fail after it fired off a second time. Also for some reason when closing the first article, the left margin was property was ignored.
ie.
First time a window is clicked: Performs open animation flawlessly
First time window's close button is clicked: Performs close animation flawlessly, returns original position
Second time SAME window is clicked: Animation fails, but opens to correct size
Second time window's close button is clicked (if visible): Nothing happens
Thank you for your patience. If you need anymore information, just ask.
EDIT
Added a jsfiddle after tinkering with Flambino's code.
http://jsfiddle.net/6RV88/66/
The articles that are not clicked need to remain where they are. Having problems achieving that now.
If you want to go for storing the offsets, you can use jQuery's .data method to store data "on" the elements and retrieve it later:
// Store offset before any animations
// (using .each here, but it could also be done in a click handler,
// before starting the animation)
$(".window").each(function () {
$(this).data("closedOffset", $(this).position());
});
// Retrieve the offsets later
$('.window').on('click', 'button.sClose', function() {
var originalOffset = $(this).data("originalOffset");
// ...
});
Here's a (very) simple jsfiddle example
Update: And here's a more fleshed-out one
Big thanks to Flambino
I was able to create the effect desired. You can see it here: http://jsfiddle.net/gck2Y/ or you can look below to see the code and some explanations.
Rather than having each article's offset be remembered, I used margins on the clicked article's siblings. It's not exactly pretty, but it works exceptionally well.
<!-- HTML -->
<section>
<article>Click!</article>
<article>Me Too</article>
<article>Me Three</article>
<article>I Aswell</article>
</section>
/* CSS */
section {
position: relative;
width: 404px;
border: 1px solid #000;
height: 100px;
overflow:hidden
}
article {
height:100px;
width:100px;
position: relative;
float:left;
background: green;
border-right:1px solid orange;
}
.expanded {z-index:2;}
//Javascript
var element = $("article");
element.on("click", function () {
if( !$(this).hasClass("expanded") ) {
$(this).addClass("expanded");
$(this).data("originalOffset", $(this).offset().left);
element.data("originalSize", {
width: element.width(),
height: element.height()
});
var aOffset = $(this).data("originalOffset");
var aOuterWidth = $(this).outerWidth();
if(!$(this).is('article:first-child')){
$(this).prev().css('margin-right',aOuterWidth)
} else {
$(this).next().css('margin-left',aOuterWidth)
}
$(this).css({'position':'absolute','left':aOffset});
$(this).animate({
left: 0,
width: "100%"
}, 500);
} else {
var offset = $(this).data("originalOffset");
var size = $(this).data("originalSize");
$(this).animate({
left: offset + "px",
width: size.width + "px"
}, 500, function () {
$(this).removeClass("expanded");
$(this).prev().css('margin-right','0')
$(this).next().css('margin-left','0')
element.css({'position':'relative','left':0});
});
}
});​

Categories