I have a code that I am working on so that it has a heading tag and an arrow floated to the right and when you click on that arrow it shows the contents of the hidden element and changes the down arrow to an up arrow. Everything seems to work fine except the links I have under the image within the toggle do not work. I cannot highlight the text for some reason so I am assuming that there is an overlap somewhere in my coding.
The JavaScript
function toggleDisplayNewark() {
document.getElementById("toggleMe").style.display == "none";
if(document.getElementById("toggleMe").style.display == "none" ) {
document.getElementById("toggleMe").style.display = "inline";
document.getElementById("toggleMe").style.visibility = "visible";
document.getElementById("arrow").style.background = "url(img/up.png) no-repeat";
} else {
document.getElementById("toggleMe").style.display = "none";
document.getElementById("arrow").style.background = "url(img/down.png) no-repeat";
}
}
The HTML
<div id='newark'>
<div class='text-heading'>
<h2>Newark</h2>
<a href="#newark" onclick="toggleDisplayNewark();">
<div id='arrow'></div>
</a>
</div>
<div id='toggleMe' style='display: none;'>
<div class='alignleft thumb-imgs'>
<img src='img/excercise.png' />
<a href='http://exercise.com/' target='_blank'>Exercise Institute</a>
</div>
<div class='clear'></div>
</div>
</div>
The CSS
#arrow {background: url(http://emf-websolutions.com/wp-content/uploads/2013/03/down.png) no-repeat; height: 27px; width: 29px; float: right; margin: -30px 10px 0 0;}
#toggleMe {display: none;}
.text-heading {-webkit-border-radius: 5px; border-radius: 5px; margin: 10px 0; width: 640px; padding: 5px 0px; background: #333; border: 1px solid red;}
.clear {clear: both;}
.thumb-imgs {width: 204px; height: 210px; padding: 5px 5px 40px 5px;}
I built this in a html file before I put it into wordpress so I could make sure that it works properly. I just can't seem to find where the problem lies. I have striped down the coding so that it would not take up so much space. The idea of this is to have a heading with an arrow in the right side to drop down this box with 3 images with a link under each one for each line.
Thanks for your help in advance.
http://jsfiddle.net/QXSXC/
remove the onclick
<a href="#newark" >
and use jQuery:
$('#newark').click(function() {
document.getElementById("toggleMe10").style.display == "none";
if(document.getElementById("toggleMe10").style.display == "none" ) {
document.getElementById("toggleMe10").style.display = "inline";
document.getElementById("toggleMe10").style.visibility = "visible";
document.getElementById("arrow10").style.background = "url(img/up.png) no-repeat";
} else {
document.getElementById("toggleMe10").style.display = "none";
document.getElementById("arrow10").style.background = "url(img/down.png) no-repeat";
}
});
You can assign a handler to #newarkwith pure JS google it if you can't use jQuery
Related
I want to set up a JS based tab navigation but somehow the Tab content doesnt show properly.
The JS code seems off but I cannot find the error.
I just want the Tab Content to show that belongs to one tab.
The Code looks like this:
function openPage(e, pageName) {
// Declare all variables
let i, content, tabLinks;
// Get all elements with class="tabs_content" and hide them
content = document.getElementsByClassName("tabs_content");
for (i = 0; i < content.length; i++) {
content[i].style.display = "none";
}
// Get all elements with class="tabs_button" and remove the class "active"
tabLinks = document.getElementsByClassName("tabs_button");
for (i = 0; i < tabLinks.length; i++) {
tabLinks[i].className = tabLinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(pageName).style.display = "block";
e.currentTarget.className += " active";
}
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
.wrapper{
display: grid;
grid-template-columns: 150px 1fr;
border:1px solid #cccccc;
padding: 15px;
margin: 0 auto;
grid-template-areas:
'nav main '
'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
flex-shrink: 0;
min-height: 400px;
}
.tabs_content {
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
}
.tabs_button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_button:active {
background: #dddddd;
}
.tabs_button:not(:last-of-type){
border-bottom: 1px solid #cccccc;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button class="tabs_button" onclick="openPage(e, 'Home')" id="defaultOpen">Home</button>
<button class="tabs_button" onclick="openPage(e, 'News')">News</button>
<button class="tabs_button" onclick="openPage(e, 'Contact')">Contact</button>
</nav>
<section class="tabs_content tabs_content--active">
<div id="Home" class="tabs_content">
<h2>Tab #1</h2>
<p>Content Page 1 </p>
</div>
<div class="tabs_content">
<h2>Tab #2</h2>
<p>Content Page 2 </p>
</div>
<div class="tabs_content">
<h2>Tab #3</h2>
<p>Content Page 3</p>
</div>
</section>
</div>
I cannot find the error, but the problem seems to be the <section>. Any help is much appreciated.
Your code has more than one problem.
The one you're currently stuck on is that e is not defined in this template bit:
<button class="tabs_button" onclick="openPage(e, 'Home')" id="defaultOpen">Home</button>
The default name of the event when you want to pass it to the function is event, not e (and you can't change it, it's a convention - you can only change its name in the function). In other words, even if in the function it's named e, you still have to reference it as event in the template.
Here are the rest of the problems, in the order I encountered them as I worked my way into making it work:
the .tabs_button:active selector is wrong. You probably want to use .tabs_button.active (since you're applying the class active to it).
you have tabs_content class on the tabs wrapper, so the entire wrapper is hidden, when you probably only want to set display:none onto its children.
you're missing the ids on second and third tab
you need width: 100% on .tabs_content
See it here:
function openPage(e, pageName) {
let i, content, tabLinks;
content = document.getElementsByClassName("tabs_content");
for (i = 0; i < content.length; i++) {
content[i].style.display = "none";
}
tabLinks = document.getElementsByClassName("tabs_button");
for (i = 0; i < tabLinks.length; i++) {
tabLinks[i].className = tabLinks[i].className.replace(" active", "");
}
document.getElementById(pageName).style.display = "block";
e.currentTarget.className += " active";
}
document.getElementById("defaultOpen").click();
.wrapper {
display: grid;
grid-template-columns: 150px 1fr;
border: 1px solid #cccccc;
padding: 15px;
margin: 0 auto;
grid-template-areas: 'nav main ' 'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
flex-shrink: 0;
min-height: 400px;
}
.tabs_content {
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
width: 100%;
}
.tabs_button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_button.active {
background: #dddddd;
}
.tabs_button:not(:last-of-type) {
border-bottom: 1px solid #cccccc;
}
body {
margin: 0;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button class="tabs_button" onclick="openPage(event, 'Home')" id="defaultOpen">Home</button>
<button class="tabs_button" onclick="openPage(event, 'News')">News</button>
<button class="tabs_button" onclick="openPage(event, 'Contact')">Contact</button>
</nav>
<section class="tabs_content--active">
<div id="Home" class="tabs_content">
<h2>Tab #1</h2>
<p>Home</p>
</div>
<div id="News" class="tabs_content">
<h2>Tab #2</h2>
<p>News</p>
</div>
<div id="Contact" class="tabs_content">
<h2>Tab #3</h2>
<p>Contact</p>
</div>
</section>
</div>
Apart from that, in my estimation, you're doing too much by JavaScript. Instead of applying inline styles, you should only apply and remove classes which, in turn, apply styles. You end up writing less code and it's easier to manage & debug.
Another thing I would personally change is replace className usage with classList, which has a more powerful and more expressive syntax.
This doesn't mean it's not doable the way you set out to do it.
The main problem when mixing stylesheet CSS with inline styles is that inline styles can only be overwritten with !important, which leads to a whole new level of problems. By only applying classes you keep the specificity battle inside your stylesheet, which is where it should take place.
Here's how I'd write it:
function openPage(e) {
const target = e.target.dataset['target'];
if (target) {
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
el.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
document.getElementById(el.dataset['target'])
.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
})
}
}
document.querySelector('.tabs_sidebar').addEventListener('click', openPage);
.wrapper {
min-height: 100vh;
display: grid;
grid-template-columns: 150px 1fr;
border: 1px solid #cccccc;
padding: 15px;
grid-template-areas: 'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
}
.tabs_content--active > div {
width: 100%;
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
display: none;
}
.tabs_content--active > div.active {
display: block;
}
.tabs_sidebar > button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_sidebar > button.active {
background: #dddddd;
}
.tabs_sidebar > button:not(:last-of-type) {
border-bottom: 1px solid #cccccc;
}
body {
margin: 0;
}
* {
box-sizing: border-box;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button data-target="Home" class="active">Home</button>
<button data-target="News">News</button>
<button data-target="Contact">Contact</button>
</nav>
<section class="tabs_content--active">
<div id="Home" class="active">
<h2>Tab #1</h2>
<p>Home</p>
</div>
<div id="News">
<h2>Tab #2</h2>
<p>News</p>
</div>
<div id="Contact">
<h2>Tab #3</h2>
<p>Contact</p>
</div>
</section>
</div>
Note I've also made changes to markup (HTML) and CSS, not just JavaScript.
As requested in comments, here's an explanation on what the JavaScript code does:
Instead of placing a click handler on each individual button, I've placed one on their parent. It has the advantage of working on future buttons as well, should your page be dynamic. If you bind on each button, once the buttons change you have to figure out which ones are new and only bind the handler on those (you don't want to bind the handler twice on the same element).
When a click is performed inside that button container, you first have to determine which button was clicked (or if the click was outside of any existing buttons). See how data attributes work here. If the click was performed on a button (if (target)), instead of having two loops (one through the buttons and one through the tabs), I only used one loop (through the buttons).
Instead of doing a classic for loop I chose to cast the NodeList returned by querySelectorAll to an array which I can then iterate using .forEach().
Note: To be totally fair, I suspect the for loop (what you used) is more performant but the difference is quite small and I tend to go for the shorter syntax:
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
// do stuff with each looped element. In this case, a button
})
Now that we have the element, let's do something with it: Add or remove the class active based on whether or not the current button's target matches the one that was clicked (which we already have saved in target const).
I personally prefer classList to className. It works on the classes array, rather than on the resulting classes string. Has add, remove and replace methods and you don't end up having to worry about adding spaces manually (or trimming them). I'm using
el.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
instead of
if (el.dataset['target'] === target) {
el.classList.add('active');
} else {
el.classList.remove('active');
}
I used the array notation to call add and remove methods on classList. (i.e: in Javascript you can write foo.bar(arg) as foo['bar'](arg) - all methods are still object properties). The advantage is that inside the array notation you can switch the method being called, based on a ternary condition, which is exactly what I'm doing above.
Using the same technique, I'm also adding/removing the active class from the corresponding tab, which I'm selecting by id.
document.getElementById(el.dataset['target'])
.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
The only thing that could slightly be improved about my code is saving the result of the ternary condition instead of doing it twice. Like this:
if (target) {
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
const isActive = el.dataset['target'] === target;
el.classList[isActive ? 'add' : 'remove']('active');
document.getElementById(el.dataset['target'])
.classList[isActive ? 'add' : 'remove']('active');
})
}
Hope that makes sense.
Hi I am trying to replace the choose file button with an image. I am using javascript to create the button but when I am inspecting the website, it shows me a html script of the button which is of type= file.
To create it, I used:
input = createFileInput(handleFile);
input.elt.style["width"] = "40%";
input.elt.style["font-size"]="3vmin";
function handleFile(file) {
print(file);
if (file.type === 'image') {
imgFile = file.data;
img = createImg(file.data);
img.hide();
canvas.image(img, 0, 0, 224, 224);
image(img, 0, 0, width, height/2);
img.remove();
}
mode = 1;
tint = false;
}
Can anyone suggest how I can change the generic button with an image.
I think you can cheat and position an image over the input, then add a click handler to the image and pass it through to the input button below.
Is this what you are trying to achieve?
const input = document.querySelector("#avatar");
const button = document.querySelector("#button");
button.addEventListener('click', event => input.click(event));
.body {
font-family: sans-serif;
}
label {
display: inline-block;
padding: 1em 0;
}
.wrapper {
position: relative;
}
#avatar {
display: block;
height: 50px;
border: 1px solid #333;
}
#button {
position: absolute;
left: 1px;
top: 1px;
}
<div class="body">
<label for="avatar">Choose a profile picture:</label>
<div class="wrapper">
<input type="file" id="avatar" name="avatar">
<img id="button" src="https://via.placeholder.com/75x50/333333/ffffff?text=Avatar"></img>
</div>
</div>
I found posts and online articles on how to do something like this but most examples are not in plain JavaScript. So this script works almost perfectly if all the sections are the same height for example 220px. So I thought I was getting closer in having this script working how I want it to work like overtime but then I realize
it had flaws when I decided to change the sections height and play around with the code more to see if it had any flaws that I was unaware of so basically this script is designed to output the name
of the sections that are visible but it is not showing the correct output for example if section 1 is the only one that is visible in the div it will say section-1 if multiple sections are visible it will say for example section-1,section-2 etc. Basically it should work like this regardless of the sections height
I know I have to change the code or altered it but I'm getting more confused the more I play around with it so how can I pull this off so I can always have the correct output? If I have to change my code completely to be able to do this then I don't mind.
This is my code
document.addEventListener('DOMContentLoaded',function(){
document.querySelector('#building').addEventListener('scroll',whichSectionsAreInSight);
function whichSectionsAreInSight(){
var building= document.querySelector('#building');
var top = building.scrollTop;
var bottom = top+building.offsetHeight;
var arr = [];
Array.prototype.forEach.call(
building.querySelectorAll('#building .sections'),
function(sections){
if ((sections.offsetTop < top && top <sections.offsetTop+sections.offsetHeight) || (sections.offsetTop < bottom && bottom < sections.offsetTop+sections.offsetHeight)){
arr.push(sections.id);
}
}
);
document.querySelector('#status').innerHTML = arr.join(',')
}
whichSectionsAreInSight();
});
h1{
margin: 0;
text-align: center;
}
#building{
background-color: gray;
height: 200px;
width: 200px;
overflow: auto;
}
.sections{
height: 80px;
width: 100%;
}
#section-1{
background-color: dodgerblue;
}
#section-2{
background-color: gold;
}
#section-3{
background-color: red;
}
#section-4{
background-color: gray;
height: 220px;
}
<p id='status'></p>
<div id='building'>
<div id='section-1' class='sections'><h1>Section 1</h1></div>
<div id='section-2' class='sections'><h1>Section 2</h1></div>
<div id='section-3' class='sections'><h1>Section 3</h1></div>
<div id='section-4' class='sections'><h1>Section 4</h1></div>
</div>
You were pretty close!
First off, you need to set the parent element to position:relative otherwise the parent that is being measured against is the document.
Also, the algorithm is simpler than what you had. Just make sure that the top of the element is less than the bottom of the parent, and the bottom of the element is greater than the top of the parent.
In your case this is offsetTop < bottom and offsetTop + offsetHeight > top
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#building').addEventListener('scroll', whichSectionsAreInSight);
function whichSectionsAreInSight() {
var building = document.querySelector('#building');
var top = building.scrollTop;
var bottom = top + building.offsetHeight;
var arr = [];
Array.prototype.forEach.call(
building.querySelectorAll('#building .sections'),
function(section) {
if (section.offsetTop < bottom && section.offsetTop + section.offsetHeight > top) {
arr.push(section.id);
}
}
);
document.querySelector('#status').innerHTML = arr.join(',')
}
whichSectionsAreInSight();
});
h1 {
margin: 0;
text-align: center;
}
#building {
background-color: gray;
height: 200px;
width: 200px;
overflow: auto;
position: relative;
}
.sections {
height: 80px;
width: 100%;
}
#section-1 {
background-color: dodgerblue;
}
#section-2 {
background-color: gold;
}
#section-3 {
background-color: red;
}
#section-4 {
background-color: gray;
height: 220px;
}
<p id='status'></p>
<div id='building'>
<div id='section-1' class='sections'>
<h1>Section 1</h1>
</div>
<div id='section-2' class='sections'>
<h1>Section 2</h1>
</div>
<div id='section-3' class='sections'>
<h1>Section 3</h1>
</div>
<div id='section-4' class='sections'>
<h1>Section 4</h1>
</div>
</div>
I am implementing a 'blades' experience in a page. When I append an additional Blade into the Container...the previous blades 'pop' down.
Q: How do I append a new element into view without effecting previous elements?
MY FIDDLE:
I created a JSFiddle...but the service is not currently available...I will append it shortly.
https://jsfiddle.net/PrisonerZ3RO/oynae1hd/4/#
MY CSS:
<style>
/** DASHBOARD CONTAINER **/
.dashboard-container { border-right: solid 1px #000; margin-top: 5px; margin-bottom: 5px; overflow-x: scroll; white-space: nowrap; width: 100%; }
.dashboard-container .widget { clear: both; display: inline-block; vertical-align: top; }
/** FORM CONTAINER **/
.form-container { border: 1px solid #ccc; border-radius: 3px; height: 500px; margin-bottom: 5px; padding: 5px; width: 500px; }
/** BLADE CONTAINER **/
.blade-container .blade { border: 1px solid #ccc; border-radius: 3px; display: inline-block; height: 506px; margin-right: 2px; padding: 2px; width: 200px; }
</style>
MY HTML:
<script id="tmplBlade" type="text/template">
<div class="blade">
Blade
</div>
</script>
<div class="dashboard-container">
<div class="widget">
<div class="form-container">
Form Controls go here
<input id="btnAppend" type="button" value="Append Blade" />
</div>
</div>
<div class="widget">
<div class="blade-container">
</div>
</div>
</div>
MY JAVASCRIPT:
<script type="text/javascript">
$(document).ready(function () {
function PageController()
{
var that = this,
dictionary = {
elements: { btnAppend: null, bladeContainer: null },
selectors: { btnAppend: '#btnAppend', bladeContainer: '.blade-container', tmplBlade: '#tmplBlade' }
};
var initialize = function () {
// Elements
dictionary.elements.btnAppend = $(dictionary.selectors.btnAppend);
dictionary.elements.bladeContainer = $(dictionary.selectors.bladeContainer);
// Events
dictionary.elements.btnAppend.on('click', that.on.click.btnAppend);
};
this.on = {
click: {
btnAppend: function (e) {
var html = $(dictionary.selectors.tmplBlade).html().trim();
var $element = $(html);
$element.hide();
dictionary.elements.bladeContainer.prepend($element);
// Slide-in
$element.show('slide', { direction: 'left' });
}
}
};
initialize();
}
var pageController = new PageController();
});
</script>
I've come across this problem before. The only way I've found to get around it is to do the following:
1) Create a .hidden class width margin-left: -200px
2) Add a CSS transition on margin-left to the .blade class
3) Apply the .hidden class to a new blade
4) Show the new blade
5) Remove the .hidden class from the new blade
Please see the following fork of your fiddle for a working solution: https://jsfiddle.net/yxL4embt/
How do I append a new element into view without effecting previous elements?
I'm not sure if I entirely get what you're asking since you'll always be affecting the other elements by moving them over when you append a new element. You can, however, prevent the pop-down effect you're seeing. The .ui-effects-wrapperadded by jQuery UI is display: block, so add the following to your CSS:
.blade-container .ui-effects-wrapper {
display: inline-block !important;
}
Then make sure your other blades are always aligned to the top of your container:
.blade-container .blade {
...
...
vertical-align: top;
}
This will bump all the blades over (right) and allow a new blade to slide in from the left.
I've been lurking w3schools for some time and studying javaScript. I've struggled for a few days with a code of which the function is to open and then close the opened menu on click again. I couldn't do this with a single , but I've managed to it with two.
I've managed to do this with the following method:
<div id="menuClosed" style="background: blue; color: white; width: 500px; height: 50px; transition: 0.3s">
<p id="menuString" style="margin: auto; text-align:center;">
Click on the button to open the menu
</p>
<button id="menuButton" onclick="changeStyle('Closed')" style="margin-left:250px;">Open</button>
<div>
<p style="font-size: 30px; text-align:center;">Bonefish</p>
</div>
<button id="menuButton2" onclick="changeStyle('Open')" style = "margin-left:250px; display: none;">Close</button>
</div>
<script>
function changeStyle(idMenu) {
//compresses OPEN and CLOSE buttons ID into a var
var menuButton = document.getElementById("menuButton");
var menuBotton2 = document.getElementById("menuButton2");
//Compresses menu DIV's ID into a var
var menuConfig = document.getElementById("menu" + idMenu);
//styles that will serve as factor for opening/closing the menu
var style1 = "background: blue; color: white; width: 500px; height: 50px; transition: 0.3s";
var style2 = "background: blue; color: white; width: 500px; height: 150px; transition: 0.3s";
//opens Menu and changes ID to "menuOpen"
if (idMenu === "Closed") {
menuConfig.style = style2;
menuConfig.id = "menuOpen";
menuButton.style = "display: none; margin-left:250px;";
menuButton2.style = "margin-left:250px; display: initial;"
}
//Closes menu and chages ID to "menuClosed"
if (idMenu === "Open") {
menuConfig.style = style1;
menuConfig.id = "menuClosed";
menuButton.style = "display: initial; margin-left:250px;";
menuButton2.style = "margin-left:250px; display: none;";
}
}
</script>
What I actually wanted to do, is to be able to both open and close the menu with the same button, but I can't figure out how.
I believe it can be done through changing <button id="menuButton" onclick="changeStyle('Closed')" style="margin-left:250px;">Open</button> changeStyle('Closed') into changeStyle('Open') and making necessary adjustments, but, again, my tries on that have failed.
Thanks by advance.
If you could use jQuery and some css, it you'll get what you want
UPDATED WITH JAVASCRIPT
var divmenu=document.getElementById('menu');
function toggleMenu(btn){
if(btn.className=='open'){
btn.className='close';
divmenu.className='close';
btn.innerText='Open';
}else{
btn.className='open';
divmenu.className='open';
btn.innerText='Close';
}
}
div{
padding:10px 0;
color: white;
transition: 0.3s;
background: blue;
}
div.open{
height: 150px;
}
div.close{
height: 20px;
overflow:hidden;
}
<div id="menu" class="close">
<p id="menuString" style="margin: auto; text-align:center;">
Click on the button to open the menu
</p>
<p style="font-size: 30px; text-align:center;">Bonefish</p>
</div>
<p style="text-align:center; margin:0; padding:5px 0;"><button type="button" class="close" onclick="toggleMenu(this);">Open</button></p>