I need to be able to change the style of a page based on a dropdown list. I have a dropdown form as an option list, and using JS that are currently selecting from CSS on the page. This needs to apply a separate stylesheet (style1.css, style2.css, etc) on click/change of the dropdown based on the dropdown list item, instead of the option select.
HTML
Option select
<div class="et-select-wrapper">
<div class="et-select-wrapper" style="margin:0">
<select id="styleSwitcher">
<option value="default">Default</option>
<option value="option2">Option2</option>
</select>
</div>
</div>
List Items -
NEEDS TO WORK WITH LIST ITEM (BELOW) INSTEAD OF THE ABOVE OPTION
LIST ITEMS
<button class="et-icon-down-arrow" data-toggle="dropdown"><span class="caret">
</span> Page Style</button>
<ul class="dropdown-menu" role="menu" id="styleSwitcher">
<li>Default</li>
<li>WCAG</li>
</ul>
JavaScript
window.onload = function() {
function addEvent(obj, type, fn) {
if (obj.attachEvent) {
obj['e' + type + fn] = fn;
obj[type + fn] = function() {
obj['e' + type + fn](window.event);
}
obj.attachEvent('on' + type, obj[type + fn]);
} else obj.addEventListener(type, fn, false);
}
function switchStyles() {
var selectedOption = this.options[this.selectedIndex],
className = selectedOption.value;
document.body.className = className;
}
var styleSwitcher = document.getElementById("styleSwitcher");
addEvent(styleSwitcher, "change", switchStyles);
}
===========================================================================
SOLUTION
I found the solution for what I needed here: http://test.unintentionallyblank.co.uk/switcher.html
By applying the function setStyleSheet to the li item in the dropdown like so:
<a data-toggle="dropdown"><button class="et-icon-eye"> Style Selector</button>
<ul class="dropdown-menu" role="menu">
<li>Arial</li>
<li>Open Sans</li>
</ul>
and using the javascript from here:
http://test.unintentionallyblank.co.uk/switcherpart1.js
which selects from a list of external stylesheets on the page, I'm able to change the pages style on select from the li list.
Your style switcher (UL) doesn't have a "change" event, it doesn't change when you click things - it's a LIST, not a form control. It's a static element on your page, like a text or something. Bind your switchStyle() on "click" event of LI-s, instead.
Also, consider jQuery. It saves you a lot of work around the events:
$("li").on("click",switchStyles);
UPDATE
I've created a minimal demo of how you can react on LI clicks:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<html>
<body>
<ul>
<li> Style 1 </li>
<li> Style 2 </li>
<li> Style 3 </li>
<li> Style 4 </li>
<li> Style 5 </li>
</ul>
<div class="colorMe">
<br><br><br>
</div>
<script>
function styleSwitcher(x) {
var colors = ["blue","green","red","purple","orange"];
$(".colorMe").css("background-color",colors[--x]);
}
$("li > a").on("click",function() {
var n = $(this).data("value");
styleSwitcher(n);
});
</script>
</body>
</html>
(Also a Fiddle for this)
Now you can apply your own method of switching styles.
UPDATE II
Here's another version with changing stylesheets:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<html>
<link id="customStyle" rel="stylesheet" href="theme.peace.css">
<body>
<ul>
<li> Ocean Blue Theme </li>
<li> Dark Theme </li>
<li> Flaming Red Theme </li>
<li> Peaceful Nature Theme </li>
<li> Steampunk Theme </li>
</ul>
<div class="colorMe">
<br><br><br>
</div>
<script>
function styleSwitcher(t) {
var cssName = "theme."+t+".css";
document.getElementById("customStyle").setAttribute("href",cssName);
}
$("li > a").on("click",function() {
var theme = $(this).data("value");
styleSwitcher(theme);
});
</script>
</body>
</html>
Make sure you have to properly named css files (like theme.ocean.css) in the proper directory - or update the name composition part if you have them somewhere else.
Related
I want to change 9 of the names in the UL to a red font using a on click button, while the other 3 names remain in a black font. And I want a button to reset the red fonts back to their original font. Can anyone help?
var title = document.getElementById("title");
var buttons = document.getElementsByClassName("btn");
var buttons = document.getElementsByClassName("btn2");
for (var btnIndex = 0; btnIndex < buttons.length; btnIndex++) {
buttons[btnIndex].onclick = function() {
title.style.color = this.getAttribute('data-color');
}
} else {
title1.style.color = this.getAttribute('data-color');
}
<ul>
<li id="title">John</li>
<li id="title">Jack</li>
<li id="title">Joe</li>
<li id="title1">Jim</li>
<li id="title">David</li>
<li id="title">Sam</li>
<li id="title1">Jay</li>
<li id="title">Frank</li>
<li id="title">Tim</li>
<li id="title">Zack</li>
<li id="title">Lewis</li>
<li id="title1">Danny</li>
<button class="btn" data-color="red">Change 9 names to red</button>
<button class="btn2" data-color="black">Reset</button>
</ul>
There are a couple of problems with your markup, which I'll address below, but to answer your actual question, you can do something like this:
// get references to the buttons
const button1 = document.querySelector('.btn');
const button2 = document.querySelector('.btn2');
// declare a function that adds the class 'red' to items matching the given selector
const select = selector => {
[...document.querySelectorAll(selector)].forEach(
element => element.classList.add('red')
);
}
// declare a function that removes the given class from all elements that currently have it
const deselect = className => {
[...document.querySelectorAll('.' + className)].forEach(
element => element.classList.remove(className)
);
}
// add a click handler to the button that invokes the
// select function above for items whose class includes 'title'
button1.addEventListener('click', () => select('.title'));
// add a click handler to the second button that removes the 'red' class from all items
button2.addEventListener('click', () => deselect('red'));
.red {
color: red;
}
<ul>
<li class="title">John</li>
<li class="title">Jack</li>
<li class="title">Joe</li>
<li class="title1">Jim</li>
<li class="title">David</li>
<li class="title">Sam</li>
<li class="title1">Jay</li>
<li class="title">Frank</li>
<li class="title">Tim</li>
<li class="title">Zack</li>
<li class="title">Lewis</li>
<li class="title1">Danny</li>
</ul>
<button class="btn" data-color="red">Change 9 names to red</button>
<button class="btn2" data-color="black">Reset</button>
a more efficient solution
This may not suit your needs, but if you just want to change the color of title items you could toggle a class on the <ul> and apply a css rule:
// get references to the button and ul
const button = document.querySelector('.btn');
const ul = document.querySelector('ul');
// toggle a class on the ul
button.addEventListener('click', () => ul.classList.toggle('red'));
/*
color 'title' items when the
ul has the 'red' class
*/
ul.red .title {
color: red;
}
<ul>
<li class="title">John</li>
<li class="title">Jack</li>
<li class="title">Joe</li>
<li class="title1">Jim</li>
<li class="title">David</li>
<li class="title">Sam</li>
<li class="title1">Jay</li>
<li class="title">Frank</li>
<li class="title">Tim</li>
<li class="title">Zack</li>
<li class="title">Lewis</li>
<li class="title1">Danny</li>
</ul>
<button class="btn">Toggle 'title' items to red</button>
markup issues
id attributes must be unique within a document. if you need to attach the same identifier to multiple elements use class instead.
<button> cannot be a child of <ul>.
First, id values must be unique, so you should be using class to
organize the similar <li> elements and use id to uniquely
identify the two buttons.
Also, the only elements that can be a child of a <ul> are <li>,
<script> and <template> elements, not <button>, so the buttons
have to be moved out of the ul.
From there, it's just a matter of setting the two buttons click handlers to the same event handler that loops over the li elements with the given class (not the buttons as you are trying to do) and adds or removes a pre-made class to the list depending on which button was clicked.
// test.js contents
document.getElementById("btn").addEventListener("click", changeColor);
document.getElementById("btn2").addEventListener("click", changeColor);
let items = document.querySelectorAll(".title");
function changeColor(event){
items.forEach(function(item){
// Figure out which button got us here
if(event.target.id === "btn"){
item.classList.add("red"); // Add red
} else {
item.classList.remove("red"); // Remove red
}
});
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Change the Certain Font Color with JavaScript</title>
<style>
.red {color:red;}
</style>
</head>
<body>
<ul>
<li class="title">John</li>
<li class="title">Jack</li>
<li class="title">Joe</li>
<li class="title1">Jim</li>
<li class="title">David</li>
<li class="title">Sam</li>
<li class="title1">Jay</li>
<li class="title">Frank</li>
<li class="title">Tim</li>
<li class="title">Zack</li>
<li class="title">Lewis</li>
<li class="title1">Danny</li>
</ul>
<button id="btn">Change 9 names to red</button>
<button id="btn2">Reset</button>
<script src="test.js"></script>
</body>
</html>
Additional notes:
You'll want to stay away from using .getElementsByClassName().
Rather than looping with counter indexes, it's much simpler to use
the Array.forEach() method on the collection returned from .querySelectorAll().
You should avoid using inline styles whenever possible as they are the hardest to override and to maintain. Instead, add, remove, or toggle the use of CSS classes with the .classList API, which is much simpler to use.
In my project, there are so many jQuery toggles needed for changing text and icons. Now I’m doing that using:
$("#id1").click(function () {
//Code to toggle display and change icon and text
});
$("#id2").click(function () {
//Same Code to toggle display and change icon and text as above except change in id
});
The problem is that I got so many to toggle, the code is quite long but all I change for each one is the id. So I was wondering if there is any way to make this simple.
Below is a sample pic. I got so many more in single page.
There are two issues here.
How to run the same action on multiple elements
How to know which element you've clicked so that you can run a relevant action on it. (most of the existing answers skip this part).
The first is to use a class for each of the elements you want to click, rather than wire up via an id. You can use a selector similar to [id^=id] but it's just cleaner to use a class.
<div id="id1" class="toggler">...
which allows you to:
$(".toggler").click(function() ...
the second is it associate the clickable with the item you want to toggle. There are many ways to do this, my preferred option is to associate them with data- attributes, eg:
<div class="togger" data-toggle="#toggle1">...
which allows you to:
$(".toggler").click(function() {
$($(this).data("toggle")).toggle();
});
The key here is that this is the element being clicked, so you can do anything else with this such as show/hide an icon inside or change colour.
Example:
$(".toggler").click(function() {
$($(this).data("toggle")).toggle();
$(this).toggleClass("toggled");
});
.toggler { cursor: pointer }
.toggled { background-color: green }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="toggler" data-toggle="#t1">T1</div>
<div class="toggler" data-toggle="#t2">T2</div>
<div class="toggler" data-toggle="#t3">T3</div>
<hr/>
<div id="t1" style='display:none;'>T1 content</div>
<div id="t2" style='display:none;'>T2 content</div>
<div id="t3" style='display:none;'>T3 content</div>
Oh,Can you use a class instead of id?
<ul>
<li class="idx">A</li>
<li class="idx">B</li>
<li class="idx">C</li>
</ul>
$(".idx").click(function(e){
//Code to toggle display and change icon and text
let target = e.target;
//You can do all what you want just base on the `target`;
});
You can store the queries in an array, and iterate over them to perform the same JQuery operation on all of them
let ids = ["#id1", "#id2", "#id3", "#randomID"]
ids.forEach((id) => {
console.log($(id).html())
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li id="id1">A</li>
<li id="id2">B</li>
<li id="id3">C</li>
<li id="randomID">D</li>
</ul>
Or (If like your example) and all of the id's are actually id1, id2, id3, ... etc.
let id = "id";
let n = 3; //amount of id's
for (let i = 1; i <= n; i++) {
console.log($("#" + id + i).html())
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li id="id1">A</li>
<li id="id2">B</li>
<li id="id3">C</li>
</ul>
You can try the below code.
var num = $("#myList").find("li").length;
console.log(num)
for(i=0;i<num;i++){
$("#id"+ i).click(function(e){
let target = e.target;
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<ul id="myList">
<li id="id1">A</li>
<li id="id2">B</li>
<li id="id3">C</li>
</ul>
So essentially I want to keep this as simple as possible, meaning no jquery or bootstrap etc... just straight javascript, HTML and CSS. This is what I have so far
Javscript:
var menuOptions= document.getElementsByClassName("nav");
var hamburger= document.getElementById("nav-btn");
function myFunction() {
hamburger.onclick= menuOptions.style.visibility= 'visible';
}
HTML:
<HTML>
<button onclick="myFunction()">
<span id="nav-btn">
<image src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>
</HTML>
CSS:
.nav {
visibility: hidden;
}
Besides just giving me a solution I would highly appreciate it if you could explain why my current method does not work and why yours does. Thanks in advance!
Two problems:
getElementsByClassName() returns a list, not a single element (though the list may contain just a single element), and that list doesn't have a .style property. You can use menuOptions[0] to access the first (and in this case only) element in the list.
You don't want to say hamburger.onclick= inside your function, because that would be assigning a new onclick handler but your function is already being called from the onclick attribute of your button. (Also, if you were trying to assign a new click handler you'd want hamburger.onclick = function() { /* something */ }.)
So the minimum change to your existing code to get it to work would be to change this line:
hamburger.onclick= menuOptions.style.visibility= 'visible';
...to this:
menuOptions[0].style.visibility = 'visible';
In context:
var menuOptions= document.getElementsByClassName("nav");
var hamburger= document.getElementById("nav-btn");
function myFunction() {
menuOptions[0].style.visibility = 'visible';
}
.nav {
visibility: hidden;
}
<HTML>
<button onclick="myFunction()">
<span id="nav-btn">
<image src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>
</HTML>
If you want repeated clicks on the button to toggle the menu display on and off then you can test the current visibility:
menuOptions[0].style.visibility =
menuOptions[0].style.visibility === 'visible' ? '' : 'visible';
Expand the following to see that working:
var menuOptions= document.getElementsByClassName("nav");
var hamburger= document.getElementById("nav-btn");
function myFunction() {
menuOptions[0].style.visibility =
menuOptions[0].style.visibility === 'visible' ? '' : 'visible';
}
.nav {
visibility: hidden;
}
<HTML>
<button onclick="myFunction()">
<span id="nav-btn">
<image src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>
</HTML>
There are a few reasons why your current setup does not function:
Document#getElementsByClassName returns a collection, and you are treating the result like a DOM element. You need to access an index like [0] to get an actual element.
Your toggle button only works one way, because visibility is set to visible but never set back to none when clicked again.
In myFunction, hamburger.onclick should not be assigned to the expression you chose. I am not sure why you tried to assign another click handler, but in order to make that work you would have needed to set it to a function () { ... }.
Now for my advice:
Use CSS classes to control whether the menu is hidden or not, rather than messing around with the style property in your JS. You can use the classList property of DOM elements to .add(), .remove(), and .toggle() a specific class when myFunction is run. I have chosen to use toggle because I think that most suits your use case.
Use element.addEventListener instead of HTML attributes like onclick.
Snippet:
var menuOptions = document.getElementsByClassName("nav")[0]
var hamburger = document.getElementById("nav-btn")
hamburger.parentNode.addEventListener('click', function myFunction() {
menuOptions.classList.toggle('hidden')
})
.nav.hidden {
visibility: hidden;
}
<button>
<span id="nav-btn">
<img src="Menugreen.png" alt="collapsable menu"/>
</span>
</button>
<div class="nav hidden">
<ul>
<li id="Programs"> Programs </li>
<li> T-Shirts </li>
<li id="About"> About </li>
</ul>
</div>
I'm writting a dropdown menu and I wanted to have the dropdown being controlled by javascript.
My dropdown has the sub menu hidden of sight max-height: 0px; and when the correspondent anchor tag is clicked, I change its max-height parameter to 400px, using the following function:
function drop_down(name) {
document.getElementById(name).style.maxHeight = "400px";
}
So far so good. The problem is that the element's max-height, stays at 400px and the sub menu does not hide. So I thought that I should target the click of the mouse and when this happens check if there is any element with 400px and change it back to 0.
$('html').click(function() {
var max_h = document.getElementsByClassName("nav_content");
var i;
for(i = 0 ; i < max_h.length ; i++)
{
if(max_h[i].style.maxHeight == "400px")
{
max_h[i].style.maxHeight = "0px";
}
}
});
What happens is that this function tracks every click, even the one used to display the sub menu. So my question is: is there a way to only activate the second function after I clicked my sub-menu? Because I always want the click that comes after the menu is displayed to close the sub menu.
HTML:
<body>
<div class="nav_container">
<nav class="nav_main">
<div class="logo">
<a href="#">
<img src="../majo.png" alt="logo">
</a>
</div>
<ul class="nav" id="nav">
<li>
Home
</li>
<li>
Consultas
<div id="nav_consul" class="nav_content">
<div class="nav_sub">
<ul>
<li>
Informação Dia a Dia
</li>
<li>
Totais Mensais
</li>
<li>
Tarifário Atual da Rede
</li>
<li>
Data específica
</li>
<li>
Atividade do Sistema
</li>
<li>
Coimas
</li>
</ul>
</div>
</div>
</li>
<li>
Simulações
<div id="nav_simul" class="nav_content">
<div class="nav_sub">
<ul>
<li>
Criar tarifa Simples
</li>
<li>
Criar tarifa Complexa
</li>
<li>
Simular com Nova Tarifa
</li>
</ul>
</div>
</div>
</li>
<li>
Preferências
<div id="nav_prefs" class="nav_content">
<div class="nav_sub">
<ul>
<li>
Lista de acessos
</li>
<li>
Alterar Password
</li>
<li>
Alterar Dados de Conta
</li>
</ul>
</div>
</div>
</li>
<li>
Log Out
</li>
</ul>
</nav>
</div>
<div id="content_main">
</div>
<footer></footer>
<script src="../js/jQuery.js"></script>
<script src="../js/user_menu.js"></script>
<script src="../js/user_nav.js"></script>
<script src="../js/user_clear_sub_menu.js"></script>
</body>
Here is an easy solution:
Create the following CSS-Styles:
.nav_content.visible {
max-height: 400px;
}
.nav_content.invisible {
max-height: 0px;
}
Set the overflow property for your nav_content to hidden:
.nav_content{
overflow: hidden;
}
Now add the class invisible to your submenus, if you want them to be invisible by default (you can do this manually in the markup or by js code):
Manually e.g.:
<div id="nav_prefs" class="nav_content invisible">
or by code (after the elements have been loaded):
$(".nav_content").addClass("invisible);
Now, if you just need to adjust your drop_down function to toggle the element's invisible/visible class:
function drop_down(dropdownID){
$('#'+dropdownID).toggleClass("visible invisible");
}
UPDATE: To make all visible submenus disappear when clicked elsewhere use this piece of code, when the window is loaded:
$(document).on('click', function (e) {
if (!$(e.target).is('.nav_item') && !$(".nav_item").has(e.target).length !== 0) {
$('.nav_content.visible').toggleClass("visible invisible");
}
});
If you only want to have one submenu visible at a time, you can use this version of your drop_down function:
function drop_down(dropdownID) {
$('.nav_content.visible').toggleClass("visible invisible");
$('#' + dropdownID).toggleClass("visible invisible");
}
A working fiddle can be found here
EDIT: Since you used jQuery in your original code, I assumed the answer can use jQuery too
You'll want to create a click handler on your document, then check the target of the click. If the target of the click has a certain class, use the menu behavior. If not, or if it's a sub-menu, close the menu.
Here's a question with multiple examples:
How do I close menu on click and when the user clicks away?
Also, I'd recommend not using max-height to hide and show. Since you're using jquery already, you could just use hide() and show().
One more thing: since you're using jquery already, you don't need to use these calls: document.getElementById(name). You can do a $("#yourId") or for document.getElementsByClassName("nav_content"); you can use $(".your-class")
It looks like you attached click event to entire document. I think you need to change only $('html').click(function() { to something like $('sub-menu-selector').click(function() { to
only activate the second function after I clicked my sub-menu
Aside to that, since it's only piece of jQuery and if you're not using it elsewhere, I would replace this with addEventListener and attachEvent, but maybe that's just me :)
In that case you can use jQuery.not() method to exclude the dropdown menu from your jQuery selection, here's what you need :
$('html').not(document.getElementsByClassName("nav_container")[0]).click(function() {
//As you can pass an element to it
You can also use the :not in your first selector like this:
$('html:not(div.nav_container))
I have a drop down box navigation menu on my website here: users.aber.ac.uk/mta2/cs15020
There were minor problems with my drop downs since css can't affect parent elements when hovering children.
This is my navigaton menu including JQuery
At the moment it is affecting all the .navlists and makes them stick.
<h1 id = "title"> Max Atkins </h1>
<ul id="menu"> <!-- Drop down navigation menu -->
<li class = "navlists">
Home
</li>
<li class = "navlists"> <!-- Main buttons -->
<a> Web Assignment </a>
<ul class = "sub-menu"> <!-- Drop downs -->
<li class = "sublists">
CV
</li>
<li class = "lastitem"> <!-- Specific styling for this link -->
Write-Up
</li>
</ul>
</li>
<li class = "navlists"> <!-- Main buttons -->
<a> Richard's Assignment </a>
<ul class = "sub-menu"> <!-- Drop downs -->
<li class = "sublists">
WordPress
</li>
<li class = "lastitem"> <!-- Specific styling for this link -->
WebShop
</li>
</ul>
</li>
</ul>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(document).ready(function ()
{
$(".navlists > a").hover(function()
{
$(this).find(".navlists > a").css("border-bottom-left-radius", "0");
$(this).find(".navlists > a").css("border-bottom-right-radius", "0");
$(".sublists > a, .lastitem > a").hover(function ()
{
$(".navlists > a").css("border-bottom-left-radius", "0");
$(".navlists > a").css("border-bottom-right-radius", "0");
});
});
$(".sublists > a, .lastitem > a").mouseleave(function ()
{
$(".navlists > a").css("border-bottom-left-radius", "15px");
$(".navlists > a").css("border-bottom-right-radius", "15px");
});
});
</script>
If I understand your question correctly. Try to use parent():
$(this).find(".navlists > a").parent().css('...','...');
you can do this purely with css
you just need to use the :hover selector on the top level li and to make selectors easier add a class to menu items that have a submenu like class="hasSub"
see this fiddle http://jsfiddle.net/Vxuph/1/