Swappable CSS theme with jQuery event handler - javascript

I'm trying to create a simple swappale CSS theme with a jQuery event handler.
As you can see, I was able to modify .outer but I don't know how to style the spans. The default theme doesn't need further customization, but I would like to change the color/size of the spans and apply a hover effect, etc. for 'mytheme'.
The HTML shouldn't be changed but the JS can be edited.
Any input is highly appreciated. How can I use [data-theme="mytheme"] with other elements?
Here's the JSFiddle
This is the HTML:
<div class="outer" data-theme="default">
<ul class="list">
<li><span>1</span></li>
<li><span>2</span></li>
<li><span>3</span></li>
<li><span>4</span></li>
<li><span>5</span></li>
<li><span>6</span></li>
<li><span>7</span></li>
<li><span>8</span></li>
<li><span>9</span></li>
<li><span>0</span></li>
</ul>
<input type="button" value="Swap Theme" id="btnSwapTheme" />
</div>
JS:
$(document).ready(function() {
//Swap Theme event handler
$('#btnSwapTheme').on('click', function(evt) {
var theme = $('div.outer').attr('data-theme');
if (theme == 'default') {
$('div.outer').attr('data-theme', 'mytheme');
} else {
$('div.outer').attr('data-theme', 'default');
}
});
});
CSS:
.outer {
text-align: center;
}
.outer input {
margin: 0.5em;
}
.outer[data-theme="default"] {
background-color: #c0c0c0;
}
.outer[data-theme="mytheme"] {
background-color: red;
}
ul.list {
width: 13em;
margin: 0;
margin-left: auto;
margin-right: auto;
text-align: center;
padding: 0;
list-style-type: none;
text-align: center;
}
ul.list li {
display: inline-block;
font-weight: bold;
border: 1px solid blue;
margin: .5em;
}
ul.list li span {
display: inline-block;
height: 3em;
width: 3em;
line-height: 3em;
cursor: pointer;
}

For example like this:
.outer[data-theme="mytheme"] span {
background-color: green;
}
.outer[data-theme="mytheme"] span:hover {
background-color: coral;
}
Demo: http://jsfiddle.net/6kyhbhus/1/
You just need to prefix any selector with .outer[data-theme="mytheme"] since this is wrapping container for your elements.

Related

JS code breaks HTML checkbox element functionality

I have a custom select list which I did with checkbox and label elements.
Then I added a little JS script that changes options on click.
It works perfectly, except you can't close it on click outside of it or its child elements.
I tried to use event listener(commented in snippet) to track if the click was done outside and if it is - change checkbox status on 'false' and it kinda worked, however, it breaks the original checkbox functionality and you can't now close it in any way except for clicking outside of it. Is there a way I can fix this?
//SELECT FROM LIST
function selectList(id) {
let selected = document.getElementById('selected');
selected.innerHTML = id;
}
//BREAKS CHECKBOX
//window.addEventListener('mouseup', function(event){
// let checkbox = document.getElementById('checkbox');
// if (event.target != checkbox && event.target.parentNode != checkbox){
// checkbox.checked = false;
// }
// });
.btn {
display: block;
width: 100px;
height: 50px;
text-align: center;
color: white;
background: black;
border-radius: 15px;
line-height: 50px;
cursor: pointer;
user-select: none;
position: relative;
}
.btn:active {
background: grey;
}
.btn ul {
display: none;
list-style: none;
color: black;
flex-direction: column;
margin: 0;
padding: 2.5px 5px;
left: 0;
top: 50px;
background: grey;
border-radius: 15px;
position: absolute;
}
.btn ul li {
display: block;
width: 90px;
height: 35px;
line-height: 35px;
background: black;
border-radius: 15px;
color: white;
margin: 2.5px 0;
}
.btn ul li:active {
background: grey;
}
/* CHECKBOX CHEKCED */
#checkbox:checked + .btn ul {
display: flex;
}
<input type="checkbox" id="checkbox">
<label class="btn" for="checkbox">
<span id="selected">SELECT</span>
<ul>
<li onclick="selectList('Opt1')">Opt1</li>
<li onclick="selectList('Opt2')">Opt2</li>
<li onclick="selectList('Opt3')">Opt3</li>
</ul>
</label>
Your strategy uses css to style the state of the dropdown based on the radio option value.
So there's no event handler in place already catching that point in time and the only approach to close the dropdown when you click outside, was using a click event handler on the main document and check for the element triggering the event to be of type HTML. Not the best approach indeed. (I edited the question to use a better approach... there's a container having the class dropdown now and event.target gets checked for having an ancestor with such class to understand if the click was fired from the dropdown itself or outside of it)
document.addEventListener('click', function(event){
if(event.target.closest('.dropdown') === null){
document.querySelector('#checkbox').checked = false;
}
});
Anyway once there, the logic just force the radio option to unchecked and restore the dropdown collapse state.
//SELECT FROM LIST
function selectList(id) {
let selected = document.getElementById('selected');
selected.innerHTML = id;
}
document.addEventListener('click', function(event){
if(event.target.closest('.dropdown') === null){
document.querySelector('#checkbox').checked = false;
}
});
//BREAKS CHECKBOX
//window.addEventListener('mouseup', function(event){
// let checkbox = document.getElementById('checkbox');
// if (event.target != checkbox && event.target.parentNode != checkbox){
// checkbox.checked = false;
// }
// });
.btn {
display: block;
width: 100px;
height: 50px;
text-align: center;
color: white;
background: black;
border-radius: 15px;
line-height: 50px;
cursor: pointer;
user-select: none;
position: relative;
}
.btn:active {
background: grey;
}
.btn ul {
display: none;
list-style: none;
color: black;
flex-direction: column;
margin: 0;
padding: 2.5px 5px;
left: 0;
top: 50px;
background: grey;
border-radius: 15px;
position: absolute;
}
.btn ul li {
display: block;
width: 90px;
height: 35px;
line-height: 35px;
background: black;
border-radius: 15px;
color: white;
margin: 2.5px 0;
}
.btn ul li:active {
background: grey;
}
/* CHECKBOX CHEKCED */
#checkbox:checked + .btn ul {
display: flex;
}
<div class="dropdown">
<input type="checkbox" id="checkbox">
<label class="btn" for="checkbox">
<span id="selected">SELECT</span>
<ul>
<li onclick="selectList('Opt1')">Opt1</li>
<li onclick="selectList('Opt2')">Opt2</li>
<li onclick="selectList('Opt3')">Opt3</li>
</ul>
</label>
</div>

How can I focus the onclick onto this icon? (HTML, CSS, JS)

I need help with figuring out how to focus the 'clicking' part of this dropdown navbar icon so that I don't have to click a little to the left of the icon (also the other navbar items) since I'm trying to recreate Mac OS's navbar.
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(e) {
if (!e.target.matches('.dropbtn')) {
var myDropdown = document.getElementById("myDropdown");
if (myDropdown.classList.contains('show')) {
myDropdown.classList.remove('show');
}
}
}
.navbar {
overflow: hidden;
background-color: #333;
font-family: Arial, Helvetica, sans-serif;
}
.navbar a {
float: left;
font-size: 12px;
color: white;
text-align: center;
padding: 10px 10px;
text-decoration: none;
}
.dropdown {
float: left;
overflow: hidden;
}
.dropdown .dropbtn {
cursor: pointer;
font-size: 12px;
border: none;
outline: none;
color: white;
padding: 5px 10px;
background-color: inherit;
font-family: inherit;
margin: 0;
}
<div class="dropdown">
<button class="dropbtn" onclick="myFunction()">
<i class="fa-solid fa-power-off"></i>
</button>
<div class="dropdown-content" id="myDropdown">
Link 1
Link 2
Link 3
</div>
</div>
IMG:
Problem
I found the source where OP came from and I am not surprised why you ran into trouble. W3Schools is a good resource because it's simple and never over explains things but at times it omits or just overlooks certain details. In the W3School example "Dropdown Menu Inside a Navigation Bar" the following segment of a CSS ruleset is wrong:
.dropbtn:focus {
background-color: red;
}
focus event only applies to the these tags:
<input>
<textarea>
<select>
<a>
It may vary between browsers but the above list is standard. So <button> is usually not focusable.
Solution
Change the <button> into an <a>
Add e.preventDefault(); to the event handler so the page won't jump when the <a> is clicked.
The rest of the changes are recommended, but not necessary. Although I strongly suggest that you don't use inline event handlers:
<button onclick="lame(this)">Don't do this</button>
Instead use:
// onevent property
document.querySelector('button').onclick = better;
OR
// event listener
document.querySelector('button').addEventListener('click', best);
const menu = document.querySelector("menu");
document.querySelector('.btn').onclick = toggleMenu;
function toggleMenu(e) {
e.preventDefault();
menu.classList.toggle("show");
}
window.onclick = function(e) {
if (!e.target.matches('.btn, .btn *') && menu.classList.contains('show')) {
menu.classList.remove('show');
}
}
html {
font: 300 2vmax/1.2 'Segoe UI';
}
nav {
display: flex;
overflow: hidden;
background-color: #333;
}
nav a {
display: block;
padding: 0.75rem 1.2rem;
color: white;
text-align: center;
text-decoration: none;
}
.dropdown {
min-width: 7.75rem;
overflow: hidden;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0;
padding: 0.75rem 1.2rem;
border: none;
outline: none;
color: white;
text-align: left;
}
.btn * {
display: block;
font-weight: 300;
}
.btn i {
padding-top: 0.15rem;
}
nav a:hover,
.btn:focus {
background-color: red;
}
menu {
position: absolute;
display: none;
min-width: 7.75rem;
margin: 0;
padding-left: 0;
background-color: #f9f9f9;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
}
menu a {
padding: 0.5rem 1.2rem;
text-align: left;
color: black;
}
menu a:hover {
background-color: #ddd;
}
.show {
display: block;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<nav>
Home
News
<div class='dropdown'>
<a href='#' class="btn"><b>Menu</b>
<i class="fa fa-chevron-circle-down"></i>
</a>
<menu>
Link 1
Link 2
Link 3
</menu>
</div>
</nav>

CSS hover no longer working when changing CSS properties through JS

I have this piece of code:
function dropdown() {
let dropdownText = document.querySelector(".dropdown-button");
let item = document.querySelector(".dropdown-items").getElementsByTagName("div")[0];
var aux = dropdownText.innerHTML;
dropdownText.innerHTML = item.innerHTML;
item.innerHTML = aux;
document.querySelector(".dropdown-items").style.display = "none";
}
.btn {
width: 150px;
height: 30px;
border-radius: 5px;
border: none;
box-shadow: 0 3px 1px 0 black;
text-align: center;
line-height: 30px;
color: black;
font-family: Consolas, monaco, monospace;
}
.dropdown {
margin: 0 50px 0 50px;
position: relative;
}
.dropdown-items {
display: none;
position: absolute;
}
.dropdown:hover .dropdown-button {
background: red;
}
.dropdown:hover .dropdown-items {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.dropdown-button {
background: orange;
font-size: 15px;
color: white;
}
.dropdown-button:hover {
cursor: pointer;
}
.dropdown-items div {
margin-top: 5px;
transform: scaleX(90%);
height: 20px;
line-height: 20px;
background: lightgray;
padding: 5px 0 5px 0;
text-align: center;
}
.dropdown-items div:hover {
cursor: pointer;
background: gray;
}
<div class="dropdown">
<button class="btn dropdown-button" type="button">Terminate</button>
<div class="dropdown-items">
<div class="btn" onclick="dropdown();">Interrupt</div>
</div>
</div>
As you can see, I am trying to make a dropdown. I also want to make it so that when I click an option in the dropdown, the dropdown items stop showing as the option has been selected. That's why I added the line document.querySelector(".dropdown-items").style.display = "none"; in the JS file as I thought the .dropdown:hover .dropdown-items part of my CSS would change back the display of those elements to visible when hovering again, but when hovering again after the first click, the dropdown does not show anymore. Why is happening and how can I fix this?
Inline styles override any stylesheet styles, as they have maximum CSS specificity.
Instead of working with inline styles (el.style.display = "none"), work with a CSS class open that you toggle. Also don't make use of inline event listeners like onclick. Those are insecure and widely considered bad practice for a whole bunch of reasons. Use addEventListener instead.
// get all the dropdowns in a NodeList
const dropdowns = document.querySelectorAll('.dropdown');
// iterate over the list
for (const dropdown of dropdowns) {
// for each dropdown, add a mouseenter and mouseleave listener
dropdown.addEventListener('mouseenter', function(event) {
dropdown.classList.add('open');
});
dropdown.addEventListener('mouseleave', function(event) {
dropdown.classList.remove('open');
});
// Now add a click listener to each <div class="dropdown-items">
// that transfers the text and closes the dropdown
dropdown.querySelector('.dropdown-items').addEventListener(
'click',
function(event) {
this.previousElementSibling.textContent = this.textContent;
dropdown.classList.remove('open');
}
);
}
.btn {
width: 150px;
height: 30px;
border-radius: 5px;
border: none;
box-shadow: 0 3px 1px 0 black;
text-align: center;
line-height: 30px;
color: black;
font-family: Consolas, monaco, monospace;
}
.dropdown {
display: inline-block;
position: relative;
}
.dropdown-items {
display: none;
position: absolute;
}
.dropdown:hover .dropdown-button {
background: red;
}
.dropdown.open .dropdown-items {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.dropdown-button {
background: orange;
font-size: 15px;
color: white;
}
.dropdown-button:hover {
cursor: pointer;
}
.dropdown-items div {
margin-top: 5px;
transform: scaleX(90%);
height: 20px;
line-height: 20px;
background: lightgray;
padding: 5px 0 5px 0;
text-align: center;
}
.dropdown-items div:hover {
cursor: pointer;
background: gray;
}
<div class="dropdown">
<button class="btn dropdown-button" type="button">Terminate</button>
<div class="dropdown-items">
<div class="btn">Interrupt</div>
</div>
</div>
<div class="dropdown">
<button class="btn dropdown-button" type="button">Terminate</button>
<div class="dropdown-items">
<div class="btn">Whatever</div>
</div>
</div>
<div class="dropdown">
<button class="btn dropdown-button" type="button">Terminate</button>
<div class="dropdown-items">
<div class="btn">Another one</div>
</div>
</div>
<div class="dropdown">
<button class="btn dropdown-button" type="button">Terminate</button>
<div class="dropdown-items">
<div class="btn">Here we go</div>
</div>
</div>

Rearranging list elements in jQuery

I am making a ul li element function like a select input from which user can select a color for the box.
Here's the code:
$(document).ready(function() {
$('.color-picker ul.color-list li.active .text').after('<span class="pull-right glyphicon glyphicon-menu-down"></span>');
$('.color-picker ul.color-list li').click(function() {
if ($(this).hasClass('active')) {
console.log(2);
$('.color-picker ul.color-list li').show();
} else {
console.log(1);
var color = $(this).find('.box').css('backgroundColor');
$('.color-picker .color-box').css({
'background-color': color
});
$('.color-picker ul.color-list li.active').removeClass('active');
$('.color-picker ul.color-list li').hide();
$(this).addClass('active');
$('.color-picker ul.color-list li.active').show();
$('.color-picker ul.color-list li .glyphicon').remove();
$('.color-picker ul.color-list li.active .text').after('<span class="pull-right glyphicon glyphicon-menu-down"></span>');
}
});
});
.color-picker {
display: flex;
margin: 20px;
border: 1px solid #d9d9d9;
padding: 10px;
width: 350px;
}
.color-picker .color-box {
display: inline-block;
background-color: black;
height: 50px;
width: 50px;
}
.color-picker ul.color-list {
display: inline-block;
margin: 0;
position: absolute;
}
.color-picker ul.color-list li {
list-style: none;
border: 1px solid #d9d9d9;
padding: 10px;
cursor: pointer;
width: 200px;
background-color: white;
}
.color-picker ul.color-list li.active {
display: block;
}
.color-picker ul.color-list li {
display: none;
}
.color-picker ul.color-list li .box {
content: "";
display: inline-block;
height: 10px;
width: 30px;
margin-right: 10px;
}
.color-picker ul.color-list li.red .box {
background-color: #c71212;
}
.color-picker ul.color-list li.blue .box {
background-color: #0d89d1;
}
.color-picker ul.color-list li.green .box {
background-color: #18c771;
}
.color-picker .as-select .select-default {
border: 1px solid #d9d9d9;
padding: 10px 20px;
width: 200px;
margin-left: 40px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="color-picker">
<div class="color-box">
</div>
<div class="as-select">
<ul class="color-list">
<li class="red active">
<span class="box"></span>
<span class="text">Red</span>
</li>
<li class="blue">
<span class="box"></span>
<span class="text">Blue</span>
</li>
<li class="green">
<span class="box"></span>
<span class="text">Green</span>
</li>
</ul>
</div>
</div>
jsFiddle
As you can see the active element remains at the same place.
(Select Blue color and click drop down again)
Is their a way I can make the selected element the first child of the list?
Leverage Flexbox then you don't need to shuffle elements at all. CSS can visually "move" them for you.
.color-picker ul.color-list {
display: inline-flex;
margin: 0;
flex-direction: column;
}
.color-picker ul.color-list li {
list-style: none;
border: 1px solid #d9d9d9;
padding: 10px;
cursor: pointer;
order:2;
width: 200px;
}
.color-picker ul.color-list li.active {
display: block;
order:1;
}
JSfiddle Demo
You can use .prependTo() to prepend the active li element to color picker element.
$(this).prependTo('.color-picker ul.color-list');
DEMO
You should use the .prepend() function.
This should work:
$('.color-list').prepend($(this));
Working JSFiddle
you can set the current element to the first place of the list with :
$('.color-picker ul.color-list').prepend($(this));
https://jsfiddle.net/ksxhwo43/
The way to make the selected element the first child of the list is detaching the li dom element of the selected item and pre-appending it in the ul dom element.
You are probably going to use:
https://api.jquery.com/detach/
"This method is useful when removed elements are to be reinserted into the DOM at a later time.
http://api.jquery.com/prepend/"
"insert at the beginning of each element in the set of matched elements"

How to use animation with append in jQuery?

How to use animation while appending new element using jQuery? I went through couple of answers here but the same method does not work for me. I used show('show') and fadeIn('slow') but it does not seem that animates the new element.
$(document).ready(function() {
$('#add-item').click(add);
function add() {
var newItem = $('#new-item-text');
var span = $('<span>', {
class: 'remove',
click: remove
});
var li = $('<li>', {
class: 'todo-item',
text: newItem.val(),
append: span,
click: completed
});
if (newItem.val()) {
$('ul.todo-list').append(li, $('li.todo-new')).fadeIn('slow');
newItem.val('');
}
}
});
.todo-list {
list-style: none;
padding: 0px;
}
.todo-item {
border: 2px solid #444;
margin-top: -2px;
padding: 10px;
cursor: pointer;
display: block;
background-color: #ffffff;
}
.todo-new {
display: block;
margin-top: 10px;
}
.todo-new input[type='text'] {
width: 260px;
height: 22px;
border: 2px solid #444;
}
.todo-new a {
font-size: 1.5em;
color: black;
text-decoration: none;
background-color: #ffffff;
border: 2px solid #444;
display: block;
width: 24px;
float: right;
text-align: center;
}
.todo-new a:hover {
background-color: #0EB0dd;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<ul class='todo-list'>
<li class='todo-item'>4L 2% Milk
<span class='remove'></span>
</li>
<li class='todo-item'>Butter, Unsalted
<span class='remove'></span>
</li>
<li class='todo-new'>
<input id='new-item-text' type='text' />
<a id='add-item' href='#'>+</a>
</li>
</ul>
To animate, you should start by hiding the elements, for instance by setting:
display: none;
Then fadeIn() will animate and set display: block;

Categories

Resources