I have installed sufee admin dashboard on my rails project. Template page is here. Demo is here
It works fine as in the demo.
This is the code for toggle:
$('#menuToggle').on('click', function(event) {
$('body').toggleClass('open');
});
You can notice that when navigating to new page the sidebar opens, even if it was collapsed. I would like it instead to persist the collapsed state. And same for open state, if it's open and I navigate to new page it should stay open. In other words sidebar state should change only when clicking the toggle. How can I achieve it?
You can try this:
$('#menuToggle').off('click').on('click', function(event) {
$('body').toggleClass('open');
});
Inspecting the site and seeing its behaviour, you have to add class="open" to the body tag, and that's all
If the default state of your sidebar is open, then on any non-dynamic page load, it will be open. You'll need to introduce some sort of persistent data storage to store the current state for the current user.
The most common choice here would be to set the cookie on the same click event. To prevent the sidebar from collapsing after the page has loaded, you could read the cookie server-side to determine whether to include the open body class or not to help prevent FOUC-type issues.
Something like this should get you started:
function setCookie(name, value){
var expires = new Date();
expires.setTime(expires.getTime() + 31536000000);
document.cookie = name + '=' + value + ';expires=' + expires.toUTCString();
}
var getCookie = function(name){
var pair = document.cookie.match(new RegExp(name + '=([^;]+)'));
return !!pair ? pair[1] : null;
};
$('#menuToggle').on('click', function(){
if( $('body').hasClass('open'){
// Menu Open: Collapse it, save Collapsed State
$('body').removeClass('open');
setCookie('menuState', 'collapsed');
} else {
// Menu Collapsed: Open it and save Open State
$('body').addClass('open');
setCookie('menuState', 'open');
}
});
Now if you need to read that cookie state in JS you can do something like the following (though this can lead to the FOUC I mentioned before, so you may want to read the cookie serverside - I'm unfamiliar with how to do that in Rails but it seems easy enough
var menuState = getCookie('menuState');
if( menuState == 'collapsed' ){
$('body').removeClass('open');
} else if( menuState == 'open' ){
$('body').addClass('open');
}
Maybe you can try this approach. just to get the idea, by the way the error on the snippet is normal, so better try it on a separate file first. you'll see it when you reload your browser.
$(document).ready(function(){
$('#menuToggle').click(function(){
if($('body').hasClass('open')){
sessionStorage.removeItem('body');
}else{
sessionStorage.setItem('body', 'open');
}
});
if(sessionStorage.getItem('body')){
$('body').addClass('open');
}else{
$('body').removeClass('open');
}
});
.sidebar{
width: 0;
background-color: #222;
height: 200px;
}
body.open .sidebar{
width: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<button id="menuToggle">Toggle</button>
<div class="sidebar"></div>
</body>
Related
Im building a new personal blog and I'm using ajax to post back to a C# Controller to get the results for pagination.
Page 2 loads with the results however, none of the javascript is reloaded because, I believe, when I partially reload the pagination part of the page, it destroys everything in the DOM and because the full page doesn't reload, the javascript isn't invoked.
So I'm looking for a bit of help on working out how to get the external javascript to run again. What it does is adds css classes, gives some fade effects etc.
success: function (data) {
if (data != null) {
var page = data
$('#blogsContainer').empty();
$('#blogsContainer').replaceWith(page);
So the success works, I clear out the blogsContainer with the new data.
I'm guessing I need to add a function after the replace to then apply everything that is in an external main.js file.
The main.js file looks like this
(function($) {
var contentWayPoint = function() {
var i = 0;
$('.ftco-animate').waypoint( function( direction ) {
if( direction === 'down' && !$(this.element).hasClass('ftco-animated') ) {
i++;
$(this.element).addClass('item-animate');
setTimeout(function(){
$('body .ftco-animate.item-animate').each(function(k){
var el = $(this);
setTimeout( function () {
var effect = el.data('animate-effect');
if ( effect === 'fadeIn') {
el.addClass('fadeIn ftco-animated');
} else if ( effect === 'fadeInLeft') {
el.addClass('fadeInLeft ftco-animated');
} else if ( effect === 'fadeInRight') {
el.addClass('fadeInRight ftco-animated');
} else {
el.addClass('fadeInUp ftco-animated');
}
el.removeClass('item-animate');
}, k * 50, 'easeInOutExpo' );
});
}, 100);
}
} , { offset: '95%' } );
};
contentWayPoint();
}
The first page has the following applied to it on page load:
<div class="col-md-4 d-flex ftco-animate fadeInUp ftco-animated">
<div class="blog-entry justify-content-end">
...
</div>
</div>
But as you can see, when I press page 2, the div is missing some key css
<div class="col-md-4 d-flex ftco-animate">
<div class="blog-entry justify-content-end">
</div>
</div>
How would I apply the missing css after the partial reload with ajax?
I hope this is clear what I am trying to do but if not, please just ask.
I think the solution may be to re-execute the contentWayPoint() function at the end of the success callback. However, its likely out of scope by then. There are two simple ways to ensure its not :
The cleanest would be to ensure that the code that sets up your pagination is inside the same (function($) {}) block in main.js - that way it will "capture" the function.
The other, dirtier way, would be to change var contentWaypoint= function... to window.contentWaypoint = function - then use window.contentWaypoint() whenever you need to invoke it. THere are much better ways to doing this, but that might get you going.
So I have a table, with which I need to be able to sort and filter (form submit) as well as being able to show and hide columns. I have a simple checkbox that does nothing but toggle the columns. I have been able to get that to work properly, but my problem now is that I need to save the state throughout the entire page lifecycle -- which doesn’t happen.
The basic column show/hide is in the page itself, right after the table:
#section Scripts {
<script type="text/javascript">
$('input[name="columnControl"]').on('switchChange.bootstrapSwitch', function (event, state) {
if (state) {
$("th.status").removeClass("columnHide");
$("td.status").removeClass("columnHide");
$("th.information").addClass("columnHide");
$("td.information").addClass("columnHide");
$("td#p1").attr('colspan', 4);
$("td#p2").attr('colspan', 6);
localStorage.setItem('columnControl', 'true');
} else {
$("th.status").addClass("columnHide");
$("td.status").addClass("columnHide");
$("th.information").removeClass("columnHide");
$("td.information").removeClass("columnHide");
$("td#p1").attr('colspan', 2);
$("td#p2").attr('colspan', 3);
localStorage.setItem('columnControl', 'false');
}
});
</script>
}
So please note: the above DOES WORK, at least in terms of showing and hiding the columns.
Now, the best method I have found so far for saving information through the page cycle is via the localstorage setting, as seen in the code above. This works splendidly for tabs on other pages, but I have been unable to get it to work for my table and bootstrap switch.
Specifically:
On page load, I want the switch to be conditional on the state of the localstorage setting. If it is true or false, the switch needs to be at the appropriate setting, and the appropriate classes need to be set for the columns. If there is no localstorage content (no True or False stored), I need the switch to be ON (true) and certain classes set (a default case).
On form submit (a GET, not a POST), I need to have the switch and all applied classes to remain the same as they were, and to NOT revert to a default case.
If the user leaves the page and returns to it, the switch and classes should remain at their last state and to not revert to the default.
The best I have been able to come up with is this:
#section Scripts {
<script type="text/javascript">
$( document ).ready(function() {
var columnState = localStorage.getItem('columnControl'); //grab the localstorage value, if any
if (columnState) { //does the localstorage value even exist?
$('input[name="columnControl"]').bootstrapSwitch('setState', columnState); //if localstorage exists, set bootstrap switch state based off of localstorage value
if (columnState == 'true') { //if localstorage value == true
$("th.status").removeClass("columnHide");
$("td.status").removeClass("columnHide");
$("th.information").addClass("columnHide");
$("td.information").addClass("columnHide");
$("td#p1").attr('colspan', 4); //set tfoot colspans
$("td#p2").attr('colspan', 6);
} else { //if localstorage value == false
$("th.status").addClass("columnHide");
$("td.status").addClass("columnHide");
$("th.information").removeClass("columnHide");
$("td.information").removeClass("columnHide");
$("td#p1").attr('colspan', 2); //set tfoot colspans
$("td#p2").attr('colspan', 3);
}
} else { //if localstorage value doesn't exist, set default values
$('input[name="columnControl"]').bootstrapSwitch('setState', true);
$("th.information").addClass("columnHide");
$("td.information").addClass("columnHide");
}
});
$('input[name="columnControl"]').on('switchChange.bootstrapSwitch', function (event, state) {
… first script, above …
});
</script>
}
The actual Bootstrap Switch is initialized by a global JS file, via
$("input[type=checkbox]").bootstrapSwitch(); // checkbox toggle
which appears in the head of the webpage.
I have two groups of columns, information and status which comprise 80% of the columns between them. Three columns have no flag for being hidden or unhidden, because they are meant to be displayed at all times.
for test if the local storage exist use :
columnState === null
You just need to fire the switchChange event after define the default value
Full exemple :
#section Scripts {
<script type="text/javascript">
$( document ).ready(function() {
$('input[name="columnControl"]').on('switchChange.bootstrapSwitch', function (event, state) {
if (state) {
$("th.status").removeClass("columnHide");
$("td.status").removeClass("columnHide");
$("th.information").addClass("columnHide");
$("td.information").addClass("columnHide");
$("td#p1").attr('colspan', 4);
$("td#p2").attr('colspan', 6);
localStorage.setItem('columnControl', true);
} else {
$("th.status").addClass("columnHide");
$("td.status").addClass("columnHide");
$("th.information").removeClass("columnHide");
$("td.information").removeClass("columnHide");
$("td#p1").attr('colspan', 2);
$("td#p2").attr('colspan', 3);
localStorage.setItem('columnControl', false);
}
});
var columnState = localStorage.getItem('columnControl'); //grab the localstorage value, if any
if (columnState === null) { //does the localstorage value even exist?
columnState = true //if localstorage value doesn't exist, set default values
}
$('input[name="columnControl"]').bootstrapSwitch('setState', columnState);
</script>
}
everyone. I'm using a cookie to save my website color style. The user can change color in real time and it will saved into his cookies. Before his choice I set default css color style like this (my.css)
.color-changing{
background-color: #43A047;
}
when you working you can choose the color with jquery,
var panel_theme = $(".color-changing");
if ($.cookie('background-color')) {
panel_theme.css("background-color", $.cookie('background-color'));
}
$("#greenColor").click(function () {
panel_theme.css('background-color', '#43A047');
$.removeCookie('background-color');
$.cookie('background-color', '#43A047', {expires: 1, path: '/'});
});
$("#redColor").click(function () {
panel_theme.css('background-color', '#d32f2f');
$.removeCookie('background-color');
$.cookie('background-color', '#d32f2f', {expires: 1, path: '/'});
});
The problem is that when you choose the color which is different from default color, with every page reload you will see the very fast flicker from default color to choosen. How I can avoid this?
My suggestion would be first to use localStorage instead of cookie. Saves cookie payload that gets sent for each and every request made to server.
Then save the actual css declaration as a style tag so you can write it in the head before the html has even finished loading. This will prevent any flicker as the style will already exist as html is rendered
Something like this before closing <head>:
<script>
var theme_style = localStorage && localStorage.getItem('theme_style');
if(theme_style){
document.write(theme_style);
}
</script>
Then to set style:
function updateUserStyle(color){
// create style tag
var style = '<style id="user_style">.color-changing{background-color: '+color + ';}</style>';
// see if user style tag already exists and replace
var $currUserStyle =$('#user_style');
if($currUserStyle.length){
$currUserStyle.replaceWith(style);
}else{
// if didn't exist add to head
$('head').append(style);
}
// store active style
localStorage.setItem('theme_style', style);
}
Usage
$("#redColor").click(function () {
updateUserStyle('#d32f2f');
});
I've been trying to implement History.js. I've got some understanding of how getting and pushing states work, however I'm having particular trouble with the data storing component of the history along with using global variables.
As a simple example, I decided to try and set up a script which would change the colour of a html box upon being clicked. This would also trigger the history - essentially creating a history for clicking the box (and its colour being changed on each state of the history).
Is there any way to update a global variable based on the data (in this case, updating i per click) supplied in History State's data?
HTML:
<div id="box">CLICK ME</div>
<button id="back">Back</button>
<button id="forward">Forward</button>
CSS:
#box {
width: 300px;
height: 300px;
background-color: black;
color: white;
vertical-align: middle;
text-align: center;
display: table-cell;
}
JavaScript:
var History = window.History;
var i = 0;
if (History.enabled) {
var State = History.getState();
History.pushState({count:i}, $("title").text(), State.urlPath);
} else {
return false;
}
// Bind to StateChange Event
History.Adapter.bind(window,'statechange', function(){
State = History.getState();
console.log(State.data, State.title, State.url);
$(this).css('background-color', getColour());
});
// Trigger the change
$("#div").on("click", function() {
i++;
History.pushState({count:i},"State " + i,"?state=" + i);
});
function getColour() {
var colours = ["red", "orange", "yellow", "green", "aqua","blue", "purple", "magenta","black"];
if (i > colours.length - 1) {
i = 0;
}
if (i < 0) {
i = colours.length - 1;
}
return colours[i];
}
$("#back").on("click", function() {
History.back();
});
$("#forward").on("click", function() {
History.forward();
});
I'm also using JQuery, ajaxify-html5.js and scrollto.js as per recommendation by other threads.
Editable JSFiddle | Viewable JSFiddle
After playing around with this a ton (and reading more questions), I've figured it out. I'll detail what the solution means to any others who come across this.
JSFIDDLE VIEW SOLUTION | JSFIDDLE VIEW SOLUTION
First here's the final code. Note that the JavaScript has document.ready extras to get it working outside of JSFiddle.
It's also worth noting I took out ajaxify-html5.js and scrollto.js out, as they weren't needed (and were breaking the code somewhere).
HTML:
<div id="box">
<div id="count"></div>
<div id="colour"></div>
</div>
<button id="back">Back</button>
<button id="forward">Forward</button>
CSS:
#box {
width: 300px;
height: 300px;
background-color: white;
color: white;
vertical-align: middle;
text-align: center;
display: table-cell;
}
button {
width: 148px;
height: 40px;
}
#count, #colour {
background-color: black;
font-family: "Consolas";
}
JavaScript:
var History = window.History;
var i = 0;
var colour = getColour();
var colourName = getColourName();
$(document).ready(function() {
if (History.enabled) {
changeHistory();
}
else {
return false;
}
// Bind to StateChange Event
History.Adapter.bind(window,'statechange', function(){
State = History.getState();
i = State.data.count;
colour = State.data.colour;
colourName = State.data.colourName;
changeHistory();
});
// Trigger the change
$(document).on("click", "#box", function() {
i = i + 1;
colour = getColour();
colourName = getColourName();
changeHistory();
});
$(document).on("click", "#back", function() {
History.back();
});
$(document).on("click", "#forward", function() {
History.forward();
});
});
function getColour() {
var colours = ["rgb(220,45,45)", "orange", "rgb(230,230,50)", "rgb(15,210,80)", "rgb(100,220,220)","rgb(50,80,210)", "rgb(140,20,180)", "rgb(230,70,110)","grey"];
if (i > colours.length - 1) {
i = 0;
}
if (i < 0) {
i = colours.length - 1;
}
return colours[i];
}
function getColourName() {
var colourNames = ["Red","Orange","Yellow","Green","Light Blue","Blue","Purle","Pink","Grey"];
return colourNames[i];
}
// Make the changes
function changeHistory () {
$("#colour").html(colourName);
$("#count").html(i);
$("#box").css('background-color', colour);
History.pushState({count:i, colour: colour, colourName: colourName},"A Shade of " + colourName,"?colour=" + colourName);
}
So going back to what I wanted to achieve with the question:
Clicking the box would add history
Each history would hold variables required to affect global variables
Its worth noting the solution specifically uses variables from each iteration of the history to power the global variables, whereas the program itself uses the global variables. The variables used to power the interface never access the ones stored in history.
Let's break up the program into separate and simpler processes and functions. Much like other history.js solutions, there's things you require to get it working:
History.getState(): Gets the latest history item "from the stack"
History.Adapter.bind(window,'statechange', function() {}: An event listener which will trigger a function when the window has a statechange (history change in this case)
History.pushState({data}, title, url): Pushes a state into the "history stack". Holds an object (data), title (of the tab/window) and url to display.
Setting the history:
When a user clicks on the box, the program should:
increment the counter (i)
change the colour and colourName
add the new history stack object in
I decided to separate the first two features from the last one. The function changeHistory() is responsible for updating the contents of the box (from global variables) and pushing a new history object in (using global variables).
changeHistory() gets called whenever I want to add a new item of history in and update the contents in the box to reflect the new history - so at launch at when the box is clicked.
When the box is clicked, the first two criteria get met. Using the existing global variables and functions, the new colour and name are retrieved and set as the global variables.
This is how it should behave:
Box Click -> Increment i, Change variables -> Push History
Listen for the history change:
Once a history change has been made (either by clicking the box, pressing back/forward buttons or browser buttons), a change needs to occur.
By creating the variable State = History.getState(), we have an easy way of accessing the data from the latest history stack object.
We'll use this data from the history object.data to assign to the global variables. After updating the variables, we'll update the view using changeHistory().
This is how the model should work:
History changed -> Update globals from history -> Update View
History change will occur whenever someone presses back, forwards or the box, accounting for all possible changes.
I am quite new with Meteor but have really been enjoying it and this is my first reactive app that I am building.
I would like to know a way that I can remove the .main element when the user clicks or maybe a better way would be to remove the existing template (with main content) and then replace with another meteor template? Something like this would be simple and straightforward in html/js app (user clicks-> remove el from dom) but here it is not all that clear.
I am just looking to learn and for some insight on best practice.
//gallery.html
<template name="gallery">
<div class="main">First run info.... Only on first visit should user see this info.</div>
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
//gallery.js
firstRun = true;
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
firstRun = false;
}
})
if (Meteor.isClient) {
function showSelectedPhoto(photo){
var container = $('#gallery');
container.fadeOut(1000, function(){
Session.set('selectedPhoto', photo);
Template.gallery.rendered = function(){
var $gallery = $(this.lastNode);
if(!firstRun){
$(".main").css({display:"none"});
console.log("not");
}
setTimeout(function(){
$gallery.fadeIn(1000);
}, 1000)
}
});
}
Deps.autorun(function(){
selectedPhoto = Photos.findOne({active : true});
showSelectedPhoto(selectedPhoto);
});
Meteor.setInterval(function(){
selectedPhoto = Session.get('selectedPhoto');
//some selections happen here for getting photos.
Photos.update({_id: selectedPhoto._id}, { $set: { active: false } });
Photos.update({_id: newPhoto._id}, { $set: { active: true } });
}, 10000 );
}
If you want to hide or show an element conditionaly you should use the reactive behavior of Meteor: Add a condition to your template:
<template name="gallery">
{{#if isFirstRun}}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{/if}}
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
then add a helper to your template:
Template.gallery.isFirstRun = function(){
// because the Session variable will most probably be undefined the first time
return !Session.get("hasRun");
}
and change the action on click:
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
Session.set("hasRun", true);
}
})
you still get to fade out the element but then instead of hiding it or removing it and having it come back on the next render you ensure that it will never come back.
the render is triggered by changing the Sessionvariable, which is reactive.
I think using conditional templates is a better approach,
{{#if firstRun }}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{else}}
gallery ...
{{/if}}
You'll have to make firstRun a session variable, so that it'll trigger DOM updates.
Meteor is reactive. You don't need to write the logic for redrawing the DOM when the data changes. Just write the code that when X button is clicked, Y is removed from the database. That's it; you don't need to trouble yourself with any interface/DOM changes or template removal/redrawing or any of that. Whenever the data that underpins a template changes, Meteor automatically rerenders the template with the updated data. This is Meteor’s core feature.