Rotate Arrow on Accordion Click - javascript

How do I get this arrow rotating when the parent accordion div is clicked? Any help would be wonderful. The idea is that as you click the accordion the arrow which is an svg file should rotate, however I can't seem to get it working
HTML
<button class="accordion">Design <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste veritatis maiores doloribus ex, culpa tenetur?</p></div>
<button class="accordion">Development <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. At rerum a voluptate nesciunt! Quam, inventore.></div>
<button class="accordion">Search Engine Optimisation <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod, blanditiis. Corporis maxime eum nemo delectus.</p></div>
<button class="accordion">Progressive Web Apps <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Culpa consequuntur obcaecati iste odit vero mollitia.</p></div>
CSS
#services .accordion{
font-size:1rem;
background-color:rgba(245, 246, 250,0.4);
cursor:pointer;
padding:18px;
width:100%;
border:none;
outline:none;
display:flex;
justify-content:space-between;
width:68vw;
text-align:right;
}
#services .active, .accordion:hover{
background-color:rgba(245, 246, 250,0.4);
color:rgba(0, 151, 230,1.0);
}
#services .panel{
background-color:rgba(245, 246, 250,0.4);
display:none;
width:100%;
justify-content:center;
text-align:center;
}
#services .caret{
width:12px;
}
JS
/*accordion*/
var acc = document.getElementsByClassName("accordion");
var arrow = document.getElementsByClassName("caret")
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function () {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "flex") {
panel.style.display = "none";
} else {
panel.style.display = "flex";
}
arrow.setAttribute("transform", "rotate(-45deg)");
});
}

This currently throws an error at arrow.setAttribute, because arrow is not set at this point. And setAttribute is not a proper way to set inline styles.
Both can be fixed easily like this:
this.querySelector('.caret').style.transform = "rotate(-45deg)";
querySelector returns the first element with that class it finds, and by calling it on this, which refers to the button, we can automatically limit it to the correct scope. And then the inline style is set using an appropriate syntax.
(var arrow = document.getElementsByClassName("caret") can then be completely removed, you don’t need it for anything else.)
If you want the arrow to rotate back, when the element is collapsed again - then don’t place this line at the end, but in both the if and the else block. Once with rotate(-45deg), and once with rotate(0) to set it back to normal (or whatever value you need for that state.)

Related

Spacing between long amount of text lines css/html/js

Let's say that i have lots of text that i dont want to be in one single huge line, how could i put some white spacings after a certain amount of words? And should i do this in the CSS file or somewhere else?
So for example: this: "You have succesfully looted the house, as the house was empty you didnt run into any trouble." to: "You have succesfully looted the house, as the house was empty you
didnt run into any trouble."
Might not look as great on this site but i think you will get the idea. The string is empty at first and will be filled by some action that happens on the page.
Specify a width on the container where your text is inside. It is better to use the max-width property since your text may be smaller than the maximal width you want. In this case it is not essential though, since the paragraph is a block element and is full width.
p {
border: 1px solid lightcoral;
}
.ch-wrap {
max-width: 60ch;
}
.px-wrap {
max-width: 200px;
}
<h1>This has no width set</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam, doloremque nihil. Illo, veritatis est ipsa cumque culpa praesentium dolor error.</p>
<h1>This has a width of 60 characters</h1>
<p class="ch-wrap">Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam, doloremque nihil. Illo, veritatis est ipsa cumque culpa praesentium dolor error.</p>
<h1>This has a width of 200px set</h1>
<p class="px-wrap">Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam, doloremque nihil. Illo, veritatis est ipsa cumque culpa praesentium dolor error.</p>
You can use <br> tag to write in a new line. If you want to put white spaces you have to move the text via CSS. For example:
HTML:
<font id="moving">Some text</font>
CSS: #moving { float: right; }
but if it not works you can try with:
#moving { position: absolute !important; float: right; }
The !important attribute makes sure that the position tag will be set on absolute.

How to hide an element when clicking in another element who shares the same classes in vanilla JavaScript?

I have created a toggle menu using vanilla javaScript because I am trying not to use jQuery as a dependency. I have been successful so far, but I want to toggle off the visibility of the other elements when clicking & opening a menu content.
HTML code for the markup:
<div class="menu-link">Menu 1</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
<br>
<div class="menu-link">Menu 2</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
<br>
<div class="menu-link">Menu 3</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
<br>
<div class="menu-link">Menu 4</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
CSS code for the styling of the toggle on/off effect of the menu content:
menu-link {
display: inline-block;
cursor: pointer;
padding: 10px;
}
.menu-content {
display: none;
}
.show {
display: block;
-webkit-animation-name: show;
animation-name: show;
}
/* Show Animation */
#-webkit-keyframes show {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes show {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.animated {
-webkit-animation-duration: 1.3s;
animation-duration: 1.3s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
JavaScript code to toggle class on/off:
// var declaration
var menuLinks = document.querySelectorAll('.menu-link');
// Loop through the array & add a click event to toggle "show" class
for (var i = 0; i < menuLinks.length; i++ ) {
menuLinks[i].addEventListener('click', function() {
if(this.nextElementSibling.className.match("show") ) {
this.nextElementSibling.classList.remove("show");
} else {
this.nextElementSibling.classList.add("show");
}
});
};
This is jsfiddle example
You need to hide other elements in the DOM for this purpose. One way to do it:
JSFiddle
// var declaration
var menuLinks = document.querySelectorAll('.menu-link');
// Loop through the array & add a click event to toggle "show" class
for (var i = 0; i < menuLinks.length; i++ ) {
var el = menuLinks[i]
el.addEventListener('click', function() {
var itemId = i;
var allMenuLinks = document.querySelectorAll('.menu-link');
if(this.nextElementSibling.className.match("show") ) {
this.nextElementSibling.classList.remove("show");
} else {
this.nextElementSibling.classList.add("show");
}
for(var j=0; j<allMenuLinks.length; j++) {
if(this==allMenuLinks[j]) {
continue;
}
allMenuLinks[j].nextElementSibling.classList.remove("show");
}
})
}
Short answer:
You can simply get reference to the element which was clicked directly on your event listener and change visibility of all elements but the one which was clicked.
for (var i = 0; i < menuLinks.length; i++ ) {
menuLinks[i].addEventListener('click', function(event) {
const clickedElement = event.target;
// remove show class from all elements but clicked element
menuLinks.forEach(function(linkEl) {
if (linkEl !== clickedElement) {
linkEl.classList.toggle("show", false);
} else {
linkEl.classList.toggle("show", true);
}
});
});
};
One problem I see here is that you are registering click listners on all the elements. You can optimize that by adding one click listener to a parent DOM node instead on each individual node.
Inside the callback function you could loop through your array menuLinks, remove the show class from all siblings and finally add it to the sibling which parent fired the click event.
Something like:
// var declaration
var menuLinks = document.querySelectorAll('.menu-link');
// Loop through the array & add a click event to toggle "show" class
for (var i = 0; i < menuLinks.length; i++) {
menuLinks[i].addEventListener('click', function(e) {
for (var a = 0; a < menuLinks.length; a++) {
if (menuLinks[a] != e.target) {
menuLinks[a].nextElementSibling.className = "menu-content animated";
}
}
e.target.nextElementSibling.className = "show";
});
};
menu-link {
display: inline-block;
cursor: pointer;
padding: 10px;
}
.menu-content {
display: none;
}
.show {
display: block;
}
/* Show Animation */
#-webkit-keyframes show {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes show {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.show {
-webkit-animation-name: show;
animation-name: show;
}
.animated {
-webkit-animation-duration: 1.3s;
animation-duration: 1.3s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
<div class="menu-link">Menu 1</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
<br>
<div class="menu-link">Menu 2</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
<br>
<div class="menu-link">Menu 3</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
<br>
<div class="menu-link">Menu 4</div>
<p class="menu-content animated">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sed quas odio facere sunt ullam quo ex amet sint in vitae.</p>
First you are creating a new function each time in loop so you should rather declare such function outside. The whole code might look like this:
var menuLinks = document.querySelectorAll('.menu-link');
var onClick = function () {
var target = this;
for (var i = 0, j = menuLinks.length; i < j; i++) {
if (target === menuLinks[i]) {
target.classList.add("show");
} else {
target.classList.remove("show");
}
}
};
// Loop through the array & add a click event to toggle "show" class
for (var i = 0, j = menuLinks.length; i < j; i++) {
menuLinks[i].addEventListener('click', onClick);
}

How to create auto popup when I access a website

I was trying to create an auto popup when I access a web store that is developed in Shopif
<SCRIPT TYPE="text/javascript">
function popup(mylink, windowname) {
if (! window.focus)return true;
var href;
if (typeof(mylink) == 'string') href=mylink;
else href=mylink.href;
window.open(href, windowname, 'width=400,height=200,scrollbars=yes');
return false;
}
</SCRIPT>
<BODY onLoad="popup('autopopup.html', 'ad')">
I have the above code for Popup Windows Opening Automatically. However, I need assistance on how to make this work on and this is the website that I am trying to work it on https://petit-tapis.co.uk
Thank you in Advance
As #Scopey said, modern browsers prevent this behavior from auto occurring. You can however add a click or if you want people to take action first before doing anything else, you can for example add an overlay that blocks any other functionality (but I can tell you that this kills user experience).
Maybe say more what your goal is. Why do you want this extra window to open? What benefit is there in doing this (what do you and what does the user get out of it)?
edit: See my comment below. I also slapped together a very simple version of what I am talking about: https://jsfiddle.net/uthhvu8d/
HTML:
<div class="wrapper">
<div class="overlay">
<form action="">
<input type="text">
<input type="text">
<button>Submit</button>
</form>
</div>
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime, inventore esse aliquam nostrum? Cupiditate provident, delectus, minus voluptatum natus fugiat.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minus temporibus vitae quibusdam maxime natus fugiat quis amet sed perferendis quod.</p>
</div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laboriosam nostrum consequatur animi quod rem eos nihil obcaecati repellat. At, accusamus.</p>
</div>
CSS:
.wrapper {
position: relative;
}
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
color: #fff;
display: none;
}
JS:
function showOverlay() {
$('.overlay').show()
}
setTimeout(showOverlay, 2000)
Thankfully, modern browsers prevent this behaviour from happening.
Any window.open must occur only as a direct result of a user triggered event - such as a mouse click or similar.
As #Scopey said browsers stop you opening a pop up window however you could use the HTML5 Dialog and the dialog overlays in the browser have a play you can even open it model if you wanted will take a little longer to get it to work but it's anotion for you
<dialog id="dialog">
<iframe src="autopopup.html" />
</dialog>

drag and drop to contenteditable element second and subsequent lines

I want to drag and drop the tag to the contenteditable="true" element string using html5 and javascript.
<script>
window.addEventListener("DOMContentLoaded", function(){
function dragStartFromPalette(event){
event.dataTransfer.setData("text/html",
'<original-element class="draggable_element">'+this.innerHTML+'</original-element>');
}
function dragEndFromPalette(event){
event.dataTransfer.effectAllowed = "copyMove";
event.dataTransfer.dropEffect = "copy";
event.dataTransfer.getData("text/plain");
event.preventDefault();
}
var light_tags = document.getElementsByClassName('draggable_element');
for(i = 0; i < light_tags.length; i++){
light_tags[i].addEventListener('dragstart', dragStartFromPalette);
light_tags[i].addEventListener('dragend', dragEndFromPalette);
};
}, false);
</script>
<style>
.draggable_element {
background-color: rgb(251, 214, 132);
}
</style>
<original-element class="draggable_element" draggable="true">Lorem</original-element>
<original-element class="draggable_element" draggable="true">ipsum</original-element>
<original-element class="draggable_element" draggable="true">dolor</original-element>
<div contenteditable="true">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Vel, illum reiciendis quas in quam eos iste inventore. Perferendis amet vel, ratione unde rerum? Quasi ea maxime incidunt, perferendis vero. Nesciunt.</div>
Here is my code https://jsfiddle.net/uzLb5xx0/
The first line will success. however, attempting to trying drag and drop to the second and subsequent lines will insert unrelated span element.
How can I insert all the elements as desired? help me ;(

Show one or more elements and their content that have been hidden

I'm stuck and not sure how to move on. I want to be able to click a tab to reveal its content. With the code I currently have, when I click a single tab, it reveals the content for all the tabs. But I just want the click to reveal the content that's associated with that single tab. I'm looking for a vanilla javascript solution.
Here's the code: http://codepen.io/anon/pen/KCJAc (inline below)
CSS:
.tab-content {
display: block;
height: 0;
opacity: 0;
overflow: hidden;
transition: all 1s ease-out;
}
.tab-active {
height: auto;
opacity: 1;
}
JavaScript:
var tabHeaders = document.getElementsByClassName('tab-header');
for (var i = 0; i < tabHeaders.length; i++) {
tabHeaders[i].addEventListener('click', activateTab);
}
function activateTab() {
var tabContents = document.getElementsByClassName('tab-content');
for (var i = 0; i < tabContents.length; i++) {
tabContents[i].classList.add('tab-active');
}
}
HTML:
<div>
<h3 class="tab-header">Tab1</h3>
<p class="tab-content">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium odio iste aliquam molestias corporis blanditiis nihil soluta sint illum quibusdam reprehenderit sed quaerat iusto maiores error iure ducimus dicta ipsum.</p>
</div>
<div>
<h3 class="tab-header">Tab2</h3>
<p class="tab-content">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium odio iste aliquam molestias corporis blanditiis nihil soluta sint illum quibusdam reprehenderit sed quaerat iusto maiores error iure ducimus dicta ipsum.</p>
</div>
You're adding tab-active to all of the tab-content elements. You just want to add it to the one following the header that's clicked: Updated Pen
var tabHeaders = document.getElementsByClassName('tab-header');
for (var i = 0; i < tabHeaders.length; i++) {
tabHeaders[i].addEventListener('click', activateTab);
}
function activateTab() {
var tabContents = this.nextElementSibling;
while (tabContents && (!tabContents.classList || !tabContents.classList.contains("tab-content"))) {
tabContents = tabContents.nextElementSibling;
}
if (tabContents) {
tabContents.classList.toggle("tab-active");
}
}
Notes:
I'm using nextElementSibling to get the next sibling that's an element since you used classList in the original, so I figure you're only using this code on fairly up-to-date browsers. If you intend to use it on older browsers, you can use nextSibling instead (and also use className rather than classList.
This is because by using document.getElementsByClassName you are getting all tab-content tabs in your page, rather than the DOM level of the clicked tab-header element. You can use the nextSibling property of tab-header to get the next DOM node beside tab-header:
function activateTab() {
this.nextSibling.classList.add("tab-active");
}
Or, if you are not sure if tab-content will definitely appear directly after tab-header you can query parentNode using querySelector:
function activateTab() {
this.parentNode.querySelector(".tab-content").classList.add("tab-active");
}
Note this last method won't work in anything lower than IE8, but then again neither will the classList property in your original question.

Categories

Resources