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');
});
Related
I added new settings for user by inheriting "res.users" model:
calendar_scale_type = fields.Selection([
('day', 'Day'),
('week', 'Week'),
('month', 'Month'),
('year', 'Year')],
'Type of calendar scaling', default='week')
in calendar view i want to read this field and set scale_type when opening this form
here in calendar_model.js file i want to read this setting from current user
and also rewrite current user setting if he chooses diferent type of scale here
how can i do this? i tried to use rpc, but i do something wrong and it didn`t work.
You can override the session_info and add calendar_scale_type to use it later in the calendar model (You will need to override the setScale function).
Example:
Add calendar_scale_type to the session info:
class Http(models.AbstractModel):
_inherit = 'ir.http'
def session_info(self):
session_info = super(Http, self).session_info()
session_info['calendar_scale_type'] = self.env.user.calendar_scale_type
return session_info
Override setScale function:
/* #odoo-module */
import CalendarModel from '#calendar/js/calendar_model';
import { session } from "#web/session";
CalendarModel.include({
setScale: function (scale) {
if (!_.contains(this.scales, scale)) {
scale = session.calendar_scale_type;
}
this._super(scale);
},
});
I'm trying to create a website builder (drag and drop page builder) and was wondering where to store the styles when someone changes the styles of an element. For example, in WordPress you can type in your own custom CSS in Customizer (image example: https://i.imgur.com/qaUiVl6.png)
In other page builders like Wix or Google Chrome Inspect Element, you can click button to enable or disable styles.
While making current/live CSS edits to the page, where and how are these styles saved? (I'm not talking about a database as the code has not been saved yet. I'm talking about while making changes onsite changes, where do these "temporary/live" CSS styles get saved?)
You can use the CSSStyleSheet APIs to generate a stylesheet in memory then use insert and delete methods to add or remove rules from the stylesheet at will. When the user is done modifying you could then pass the generated stylesheet back server side to save perm.
Ref docs can be found here:
https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#Methods
Compatability is IE9+ and all other modern browsers so it has good coverage.
Quick and dirty example below.
var style = (function() {
// Create the <style> tag
var style = document.createElement("style");
// Add the <style> element to the page
document.head.appendChild(style);
return style;
})();
function AddRule(){
//append rule from textbox to ss here
style.sheet.insertRule(document.getElementById("cssIn").value, 0);
document.getElementById("appliedRules").innerHTML = '';
var rules = style.sheet.cssRules;
for (var r in rules) {
if(rules[r].cssText){
document.getElementById("appliedRules").innerHTML += '<br>' + rules[r].cssText;
}
}
}
//enable this to see your special prize in the console
//console.log(style.sheet);
<div class="test"> here we go</div>
Add Rule: <input type="text" id="cssIn" value=".test {color:blue}">
<button type="button" onClick="AddRule();">Add</button>
<div id="appliedRules"></div>
Here is a simple proof-of-concept that demonstrates how this can be done using pure javascript. Just click the save button to see the CSS in the textarea get applied to the page. The CSS is just stored as the input value of the textarea element. You can also make it more complex by using localStorage and an iframe or shadow dom so you only affect a "preview" pane. But this is just a demonstration.
function saveStyles() {
document.querySelector('#style-container').innerHTML = document.querySelector('#style-input').value;
}
#style-input {
width: 100%;
box-sizing: border-box;
display: block;
margin-bottom: 8px;
}
<style id="style-container"></style>
<textarea id="style-input" rows="5">body{background:red;}</textarea>
<button onclick="saveStyles()">Save</button>
Here's an alternative that puts the stylesheet into memory and loads it via a blob URL.
This behaves a bit more like a real stylesheet than inline styles do in some edge cases, which may be desirable in some cases. It can also work on a webpage that blocks inline styles via a Content Security Policy (provided blob URL's are allowed).
(function() {
var styles = document.getElementById('styles');
var save = document.getElementById('save');
var link = null;
function getLink() {
if (!link) {
link = document.createElement('link');
link.rel = 'stylesheet';
document.head.appendChild(link);
}
return link;
}
save.addEventListener('click', function() {
var link = getLink();
if (link.href) {
URL.revokeObjectURL(link.href);
}
link.href = URL.createObjectURL(new Blob([styles.value], {type: 'text/css'}));
});
})();
#styles {
display: block;
width: 95%;
}
<textarea id="styles" rows="5">body {
background: green;
}
</textarea>
<button id="save">Save</button>
The answers here focus on the methods for building a stylesheet and adding css rules using common methods browsers provide as part of the DOM api. These are the underlying function calls that any UI framework on the web will use.
But when you ask, "Where is this stored?". In a sense, you are asking how is the "state" managed. If you look at original post jQuery/web app building frameworks, like Backbone.js -- its motto was, "Get your model out of the DOM". So generally the "elements" of the ui-building tools will themselves be represented as component/models.
If you look at view-centric frameworks, like React or Vue, more complex web apps will use a framework like Redux to handle "state", which is stored in single object store. This represents the current options of all the components on the page.
So ultimately, a non-toy WYSIWYG web editor will likely treat each "element" as a component that renders its styles based on an inputted state.
This coupled with a controlled and predictable way to change state, allows for the managing of complex UI. For instance, the click method would trigger an action that acts like the name of an event handler, triggering the functions (in redux world, reducers), which ultimately changes the state, in our example, color of the element.
The low-level calls to the DOM that facilitate this in a complex web-app/web-editor would be abstracted away.
Based on the discussion, I can suggest to use separate styles for each element id. Here is a sketch.
<script>
function setStyle(id, style_text)
{
var style_id = "style_" + id;
var style_forId = "#" + id + " " + style_text;
var styleDom = document.getElementById(style_id);
if(!styleDom)
{
styleDom = document.createElement('style');
styleDom.type = 'text/css';
styleDom.id = style_id;
styleDom.innerHTML = style_forId;
document.getElementsByTagName("head")[0].appendChild(styleDom);
}
else
{
styleDom.innerHTML = style_forId;
}
}
</script>
<button id="myButton1" type="button" >My Button 1</button>
<button id="myButton2" type="button" >My Button 2</button>
<br>
<button onclick="setStyle('myButton1', '{color:red}'); "> Set Red color for myButton1 </button>
<br>
<button onclick="setStyle('myButton2', '{color:red}'); "> Set Red color for myButton2 </button>
<br>
<button onclick="setStyle('myButton1', '{color:green}'); "> Set Green color for myButton1 </button>
<br>
<button onclick="setStyle('myButton2', '{color:green}'); "> Set Green color for myButton2 </button>
<br>
Great Answers already just putting a different viewpoint out there.
Simple Version
Using CSSStyleSheet
const style = document.createElement("style");
document.head.appendChild(style);
style.sheet.insertRule(`
header {
background: 'black'
}
`, 0);
Real World
I would take this simple idea and control it through a data structure like this.
// A JS Object to control and modify CSS styles on.
const css = {
header: {
background: 'black',
border: '2px green solid',
'font-size': '12px'
}
}
// Converts JS Object to CSS Text (This is not battle tested)
function objToCss(style) {
return Object.entries(style).reduce((styleString, [propName, propValue]) => {
propName = propName.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
if (typeof propValue === 'object') {
return `${styleString}${propName}{${objToCss(propValue)}}`;
} else {
return `${styleString}${propName}:${propValue};`;
}
}, '')
}
// Setup
const style = document.createElement("style");
document.head.appendChild(style);
style.sheet.insertRule(objToCss(css), 0);
// Updates
css.header.border = '1px blue solid';
style.sheet.replace(objToCss(css), 0);
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>
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>
}
I have 3 boxes and once user hovers any, if changes the content of the big main div from default to the related div via featVals hash table
At the if ($('#estate-feature, #carrier-feature, #cleaning-feature').is(':hover')) { part of my code, I want to check if any of these 3 div boxes are currently hovered, if not display the default content (defaultFeat variable).
However I am getting Uncaught Syntax error, unrecognized expression: hover error from Google Chrome Javascript Console.
How can I fix it ?
Regards
$('#estate-feature, #carrier-feature, #cleaning-feature').hover(function () {
var currentFeatCont = featVals[$(this).attr('id')];
headlineContent.html(currentFeatCont);
}, function () {
headlineContent.delay(600)
.queue(function (n) {
if ($('#estate-feature, #carrier-feature, #cleaning-feature').not(':hover')) {
$(this).html(defaultFeat);
}
n();
})
});
:hover isn't an attribute of the element. Also, you are binding to the hover out there so you know that you have left the hover and can restore the default content. If you want the hover-triggered content to remain for a period after the point has left the trigger element then you'll either need to assume that you aren't going to roll over another trigger or implement a shared flag variable that indicates if the default text restore should be halted. e.g.
var isHovered = false;
$('#estate-feature, #carrier-feature, #cleaning-feature').hover(
function() {
var currentFeatCont = featVals[$(this).attr('id')];
headlineContent.html(currentFeatCont);
isHovered = true;
},
function() {
isHovered = false;
headlineContent.delay(600)
.queue(function(n) {
if (!isHovered) {
$(this).html(defaultFeat);
}
n();
})
}
);