I'm currently working on a project with p5.js and have come against a problem in javascript. I built buttons in javascript to help make certain functions work and I'm looking for a way to style the buttons. For the rest of the project I was using bootstrap so I'm trying to find a way to style it as similar as I can to the rest of the rest off the CSS if possible. The buttons code in javascript follows this.
button1 = createButton('start');
button2 = createButton('stop');
button3 = createButton('play');
button4 = createButton('save');
button1.mousePressed(start_);
button2.mousePressed(stop_);
button3.mousePressed(play_);
button4.mousePressed(save_);
createButton returns a p5.Element object which has a .class() method to set the class property on it. Classes can then be used for styling, either with bootstrap or custom CSS. For example:
let btn;
function setup() {
noCanvas();
const cls = "maroon-bg white-text";
btn = createButton("hello world");
btn.class(cls);
btn.mouseClicked(() => {
btn.class(btn.class() ? "" : cls);
});
}
.maroon-bg {
background-color: maroon;
}
.white-text {
color: white;
}
button {
padding: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
I prefer separating CSS and JS because it promotes reuse and keeps your JS clean, but if you want to inline your styles, you can use .style():
let btn;
function setup() {
noCanvas();
btn = createButton("hello world");
btn.style("background-color", "maroon");
btn.style("color", "white");
btn.style("padding", "1em");
btn.mouseClicked(() => {
if (btn.style("color") === "rgb(0, 0, 0)") {
btn.style("color", "white");
btn.style("background-color", "maroon");
}
else {
btn.style("background-color", "");
btn.style("color", "");
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
This is clearly awkward for this use case, but it can come in handy in other situations and is shown for completeness.
Related
I have created this lightbulb exercise that toggles the SRC of the lightbulb, the background color, as well as the color of the text. As an extra challenge, I'm trying to see if I can condense my code using the toggle() method as I have 3 separate functions. Does anyone know if this is possible and how I could accomplish this?
<h1 class="title-text" id="title-text">Click the lightbulb to turn it on or off!</h1>
<img id="lightbulb" onclick="toggleLight();toggleColor();toggleText()" src="/personal-projects/pic_bulbon1.gif">
<script>
let lightbulb = document.getElementById("lightbulb");
let titleText = document.getElementById("title-text");
function toggleLight() {
if (lightbulb.src.match("bulbon1")) {
lightbulb.src = "/personal-projects/pic_bulboff1.gif"
} else {
lightbulb.src = "/personal-projects/pic_bulbon1.gif"
}
}
function toggleColor() {
if (lightbulb.src.match("bulboff1")) {
document.body.style.background = "black";
} else {
document.body.style.background = "#FEDD00";
}
}
function toggleText() {
if (lightbulb.src.match("bulboff1")) {
titleText.style.color = "white";
} else {
titleText.style.color = "black";
}
}
</script>
</body>
</html>
The If loops work fine. I just want to know how I could use toggle if its possible. All the tutorials I find for this type of thing involve jquery.
Using toggle method
You want to first create a class for toggling in your css. Each element you want to toggle should have a default state and a toggled state. Toggling will add/remove a single class.
See snippet below
Snippet
const LightBulb = document.getElementById("lightbulb");
const toggleLight = () => {
LightBulb.classList.toggle("lightBulbOn");
document.body.classList.toggle("bodyLightOn");
};
body {
background-color: black;
}
body,
body .title-text {
color: white;
}
body.bodyLightOn {
background-color: #fedd00;
}
body.bodyLightOn .title-text {
color: black;
}
#lightbulb::before {
content: url("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Gluehlampe_01_KMJ.png/340px-Gluehlampe_01_KMJ.png");
}
.lightBulbOn::before {
content: url("https://cdn.mos.cms.futurecdn.net/HaPnm6P7TZhPQGGUxtDjAg-320-80.jpg") !important;
}
<h1 class="title-text" id="title-text">Click the lightbulb to turn it on or off!</h1>
<div id="lightbulb" class="" onclick="toggleLight();">
</div>
Codepen
I am still fairly new to JavaScript and am trying to deepen my understanding of it through mini projects.
In this counter project, I have managed to get the result what I want:
After clicking the "add" button, the counter increment would increase and change color to green.
After clicking the "subtract" button, the counter increment would decrease and change color to red.
Below would be my JavaScript code:
//create variables to hold the HTML selectors
var counterDisplay = document.querySelector('.counter-display');
var counterMinus = document.querySelector('.counter-minus');
var counterPlus = document.querySelector('.counter-plus');
//create variable for the counter
//what we'll use to add and subtract from
var count = 0;
//create a function to avoid code redundancy
function updateDisplay(){
counterDisplay.innerHTML = count;
};
function toGreen(){
document.querySelector('.counter-display').style.color = "green";
};
function toRed(){
document.querySelector('.counter-display').style.color = "red";
};
/*-------------------------------------------*/
updateDisplay();
//EventListeners
counterPlus.addEventListener("click", () =>{
count++;
updateDisplay();
toGreen();
});
counterMinus.addEventListener("click", () =>{
count--;
updateDisplay();
toRed();
});
I separated the color functions but I feel like there's a cleaner way to write this code i.e conditionals like if statements, however, as I'm still learning I don't fully know how to implement this.
**As mentioned, I'm still learning so a thorough explanation/laymen's terms is greatly appreciated!
Please let me know for any clarifications or if more info is needed!
Thank you in advance!
EDIT:
Thank you those who have taken the time to help me in sharing their solutions!
The code you wrote is quite long but it does the job
I'm not sure what do you want exactly, but here are few notes :
Use HTML onclick Event instead:
Instead of adding event listener from javascript you can add it in the HTML code like so: <button onclick="myFunction()">Click me</button>, and whenever the button is clicked myFunction() will be called.
You can also pass the button as a parameter, for example
function myFunction(element) {
element.style.backgroundColor = "red";
}
<button onclick="myFunction(this)">Click me</button>
Use let instead of var:
Variables declared by var keyword are scoped to the immediate function body (hence the function scope) while let variables are scoped to the immediate enclosing block denoted by { } (hence the block scope).
find more info here: What's the difference between using “let” and “var”?
You need to make the following few changes:
Make use of let and const. If it doesn't change its value in the future then use const else let.
You can create an array of buttons (in the below example buttons) so that you can loop over and add event listener on all buttons. So that you don't need to add an event listener on each one of the buttons.
You can use data attribute to recognize which type of button it is.
Get rid of multiple functions toRed or toGreen, instead make a single function that will change the color of text with only one function changeTextColor.
If you just need to change the text of the HTML element then better to use textContent in updateDisplay function.
see innerText vs innerHTML vs label vs text vs textContent vs outerText
//create variables to hold the HTML selectors
const counterDisplay = document.querySelector(".counter-display");
const counterMinus = document.querySelector(".counter-minus");
const counterPlus = document.querySelector(".counter-plus");
//create variable for the counter
//what we'll use to add and subtract from
let count = 0;
//create a function to avoid code redundancy
function updateDisplay() {
counterDisplay.textContent = count;
}
function changeTextColor(color) {
counterDisplay.style.color = color;
}
/*-------------------------------------------*/
//EventListeners
const buttons = [counterMinus, counterPlus];
buttons.forEach((btn) => {
btn.addEventListener("click", (e) => {
const btnType = e.target.dataset.type;
if (btnType === "sub") {
count--;
changeTextColor("red");
} else {
count++;
changeTextColor("green");
}
updateDisplay();
});
});
.counter-display {
background-color: black;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
font-size: 2rem;
font-weight: 700;
text-align: center;
color: whitesmoke;
}
.button-container {
display: flex;
gap: 1rem;
justify-content: center;
}
.button-container>* {
padding: .75rem 3rem;
font-size: 2rem;
border: none;
border-radius: 4px;
}
.counter-minus {
background-color: red;
}
.counter-plus {
background-color: green;
}
<div class="counter-display"> 0 </div>
<div class="button-container">
<button class="counter-minus" data-type="sub">-</button>
<button class="counter-plus" data-type="add">+</button>
</div>
How about something more app based? You can break your whole thing down into a couple key components with a little glue get something that you can easily build on. Its not very different than what you already have, it just sprinkles in some tried and true patterns.
const widget = (element) => {
const $minus = element.querySelector('.counter-minus');
const $plus = element.querySelector('.counter-plus');
const $display = element.querySelector('.counter-display');
let state = {
color: 'black',
count: 0
};
const update = (next) => {
state = next;
render();
};
const render = () => {
$display.innerText = state.count;
$display.style.color = state.color;
};
const onMinusClick = () => {
update({ color: 'red', count: state.count - 1 });
};
const onPlusClick = () => {
update({ color: 'green', count: state.count + 1 });
};
$minus.addEventListener('click', onMinusClick);
$plus.addEventListener('click', onPlusClick);
render();
return () => {
$minus.removeEventListener('click', onMinusClick);
$plus.removeEventListener('click', onPlusClick);
};
};
widget(document.querySelector('#app1'));
widget(document.querySelector('#app2'));
widget(document.querySelector('#app3'));
div {
margin-bottom: .5rem;
}
<div id="app1">
<button class="counter-minus">-</button>
<span class="counter-display"></span>
<button class="counter-plus">+</button>
</div>
<div id="app2">
<button class="counter-minus">-</button>
<span class="counter-display"></span>
<button class="counter-plus">+</button>
</div>
<div id="app3">
<button class="counter-minus">-</button>
<span class="counter-display"></span>
<button class="counter-plus">+</button>
</div>
First thing you probably notice is that i have multiple instance running of my app. I can do this because I dont rely on global state. You can see that when you call widget, it holds its own variables.
I have state as an object and the only thing that can write to state is the update function. You had something very similar except i combined my state into a single variable and have 1 function in charge of writing to state, and then reacting to it i.e. rendering.
Then I have some event handlers being attached that are just calling update with how they want the state to look. This of course triggers a render to keep the ui consistent with the state. This 1 way data flow is very important to keep code clear. The update function can be more sophistacted and do things like except partial state values and spread state = { ...state, ...next}. it can get pretty crazy.
Lasty i return a function. You can return anything in here, you might want to return an api for a user to interact with you widget with. For example you have a calendar and you want to be able to set the date outside of the widget. You might return { setDate: (date) => update({ date }) } or something. However i return a function that will clean up after the widgets by removing event listeners so garbage collection can reclaim any memory i was using.
I have a Language select-option. If I choose Arabic then it will change the direction.
* {
direction: rtl!important;
}
While I am using this, then the whole direction changed to right to left. But how can I do that with Methods?
methods: {
languageSelection() {
if(this.lang == 'arb') {
document.getElementsByTagName("*")[0].style.direction = "rtl!important";
document.getElementsByTagName("html")[0].style.direction = "rtl!important";
document.getElementsByTagName("body")[0].style.direction = "rtl!important";
}
}
}
The above code is not working!
If I can add a CSS file then it will be better for me. For example:
languageSelection() {
if(this.lang == 'arb') {
// active a css file like: style-rtl.css
}
}
But how is this possible?
Ok. So when you use getElementsByTagName("*")[0] you will probably get a handle to a <html> element. So the same element you're accessing in the next line.
To change all elements direction you would have to iterate over the collection:
const elems = document.getElementsByTagName("*")
for (let elem of elems) {
elem.style.direction = 'rtl'
}
But this will still include <script>, <style>, <meta> tags, which is not the best solution.
My solution
I would create class in the css
html.is-rtl * {
direction: rtl;
}
then just toggle the class when you select the language which is read from right to left.
languageSelection() {
if (this.lang == 'arb') {
document.querySelector('html').classList.add('is-rtl')
}
}
Could create a stylesheet and append it to the head:
languageSelection() {
if(this.lang == 'arb') {
const style = document.createElement('style');
style.innerHTML = `*{direction:rtl!important}`;
document.head.appendChild(style);
}
}
You should manage it inside App.vue. add custom css class based on chosen language to #App element.
<template>
<div id="app" :class="{'dir-rtl': isRtl}">
...
</div>
</template>
<script>
export default {
computed: {
isRtl () {
return this.lang === 'arb'
}
}
}
</script>
<style lang="scss">
.dir-rtl {
direction: rtl !important;
}
</style>
best place to save and change lang is vuex store.
I'm using AG Grid on a website. When the user clicks a cell, it is focused and gets a blue outline.
I need to remove this focus when the user clicks certain other elements on the site, but I don't know how to do it. Is there a method or property to set for that?
Add the following snippet to your css
.ag-cell-focus, .ag-cell {
border: none !important;
}
Example - https://next.plnkr.co/edit/xO5N5u84U8n4HgK5
Angular2+ DEMO
ngAfterViewInit(){
let body = document.body;
body.addEventListener("mouseup", (e) => {
let container = this.agGrid._nativeElement;
if (!container.contains(e.target))
{
this.gridApi.clearFocusedCell();
}
})
}
JavaScript DEMO
var body = document.body;
body.addEventListener("mouseup", (e) => {
let gridDiv = document.querySelector('#myGrid')
if (!gridDiv.contains(e.target))
{
gridOptions.api.clearFocusedCell();
}
})
Apple below code to the global style.css file
.ag-cell-focus {
--ag-range-selection-border-color: transparent !important;
}
Alright, I'm creating a system for my webpage that allows users to change the theme. How I want to accomplish this is by having all the colors as variables, and the colors are set in the :root part of the CSS.
What I want to do is change those colors via JavaScript. I looked up how to do it, but nothing that I attempted actually worked properly. Here's my current code:
CSS:
:root {
--main-color: #317EEB;
--hover-color: #2764BA;
--body-color: #E0E0E0;
--box-color: white;
}
JS:
(Code to set the theme, it's ran on the click of a button) - I didn't bother adding the :root change to the other 2 themes since it doesn't work on the Dark theme
function setTheme(theme) {
if (theme == 'Dark') {
localStorage.setItem('panelTheme', theme);
$('#current-theme').text(theme);
$(':root').css('--main-color', '#000000');
}
if (theme == 'Blue') {
localStorage.setItem('panelTheme', 'Blue');
$('#current-theme').text('Blue');
alert("Blue");
}
if (theme == 'Green') {
localStorage.setItem('panelTheme', 'Green');
$('#current-theme').text('Green');
alert("Green");
}
}
(Code that is ran when the html is loaded)
function loadTheme() {
//Add this to body onload, gets the current theme. If panelTheme is empty, defaults to blue.
if (localStorage.getItem('panelTheme') == '') {
setTheme('Blue');
} else {
setTheme(localStorage.getItem('panelTheme'));
$('#current-theme').text(localStorage.getItem('panelTheme'));
}
}
It shows the alert, but does not actually change anything. Can someone point me in the right direction?
Thank you #pvg for providing the link. I had to stare at it for a little to understand what was going on, but I finally figured it out.
The magical line I was looking for was this:
document.documentElement.style.setProperty('--your-variable', '#YOURCOLOR');
That did exactly what I wanted it to do, thank you very much!
For those who want to modify the actual style sheet the following works:
var sheet = document.styleSheets[0];
sheet.insertRule(":root{--blue:#4444FF}");
More info at here: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule
I think this is cleaner and easier to remember.
to set/get css variables to/from :root
const root = document.querySelector(':root');
// set css variable
root.style.setProperty('--my-color', 'blue');
// to get css variable from :root
const color = getComputedStyle(root).getPropertyValue('--my-color'); // blue
Example: setting multiple variables all at once
const setVariables = vars => Object.entries(vars).forEach(v => root.style.setProperty(v[0], v[1]));
const myVariables = {
'--color-primary-50': '#eff6ff',
'--color-primary-100': '#dbeafe',
'--color-primary-200': '#bfdbfe',
'--color-primary-300': '#93c5fd',
'--color-primary-400': '#60a5fa',
'--color-primary-500': '#3b82f6',
'--color-primary-600': '#2563eb',
'--color-primary-700': '#1d4ed8',
'--color-primary-800': '#1e40af',
'--color-primary-900': '#1e3a8a',
};
setVariables(myVariables);
To use the values of custom properties in JavaScript, it is just like standard properties.
// get variable from inline style
element.style.getPropertyValue("--my-variable");
// get variable from wherever
getComputedStyle(element).getPropertyValue("--my-variable");
// set variable on inline style
element.style.setProperty("--my-variable", 4);
I came here looking how to toggle the :root color-scheme with JavaScript, which sets the browser to dark mode (including the scroll bars) like this:
:root {
color-scheme: dark;
}
using the #Daedalus answer above, this is how I implemented my dark mode detection from user preference:
const userPrefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
const preferredTheme = userPrefersDarkMode ? 'dark' : 'light';
document.documentElement.style.setProperty("color-scheme", preferredTheme);
or with saved toggle:
const savedTheme = localStorage.getItem('theme');
if (savedTheme == 'dark') {
thisTheme = 'light'
}
else {
thisTheme = 'dark'; // the default when never saved is dark
}
document.documentElement.style.setProperty("color-scheme", thisTheme);
localStorage.setItem('theme', thisTheme);
see also the optional meta tag in the header:
<meta name="color-scheme" content="dark light">
old jquery magic still working too
$('#yourStyleTagId').html(':root {' +
'--your-var: #COLOR;' +
'}');
TL;DR
A solution to the problem could be the below code:
const headTag = document.getElementsByTagName('head')[0];
const styleTag = document.createElement("style");
styleTag.innerHTML = `
:root {
--main-color: #317EEB;
--hover-color: #2764BA;
--body-color: #E0E0E0;
--box-color: white;
}
`;
headTag.appendChild(styleTag);
Explanation:
Although #Daedalus answer with document.documentElement does the job pretty well, a slightly better approach is to add the styling into a <style> HTML tag (solution proposed).
If you add document.documentElement.style then all the CSS variables are added into the html tag and they are not hidden:
On the other hand, with the proposed code:
const headTag = document.getElementsByTagName('head')[0];
const styleTag = document.createElement("style");
styleTag.innerHTML = `
:root {
--main-color: #317EEB;
--hover-color: #2764BA;
--body-color: #E0E0E0;
--box-color: white;
}
`;
headTag.appendChild(styleTag);
the HTML tag will be cleaner and if you inspect the HTML tag you can also see the :root styling as well.
Read only, retrieve all CSS --root rules in an array, without using .getComputedStyle().
This may allow to retrieve values before full DOM content load, to create modules that use global root theme variables, but not via CSS. (canvas context...)
/* Retrieve all --root CSS variables
* rules into an array
* Without using getComputedStyle (read string only)
* On this example only the first style-sheet
* of the document is parsed
*/
console.log(
[...document.styleSheets[0].rules]
.map(a => a.cssText.split(" ")[0] === ":root" ?
a.cssText.split("{")[1].split("}")[0].split("--") : null)
.filter(a => a !== null)[0]
.map(a => "--"+a)
.slice(1)
)
:root {
--gold: hsl(48,100%,50%);
--gold-lighter: hsl(48,22%,30%);
--gold-darker: hsl(45,100%,47%);
--silver: hsl(210,6%,72%);
--silver-lighter: hsl(0,0%,26%);
--silver-darker: hsl(210,3%,61%);
--bronze: hsl(28,38%,67%);
--bronze-lighter: hsl(28,13%,27%);
--bronze-darker: hsl(28,31%,52%);
}
/*My style*/
:root {
--hl-color-green: green;
}
/*My update*/
* {
--hl-color-green: #0cc120;
}