I need to fire onblur event when I click outside "aside panel", in order to make the panel close if user clicks outside navigation panel.
Though I'm using React JS - but for simplicity I've written example in pure JavaScript.
Lately I've written something similar and everything works fine, but in this case it's not working, may be because of position fixed.
var d = document.getElementById("fname");
d.addEventListener("blur", myFunction);
function myFunction() {
alert("Input field lost focus.");
}
var state = true;
function myFunction2() {
state = !state;
if (state) {
d.className = "header__aside";
} else {
d.className += " header__aside--open";
}
}
.main {
background-color: violet;
padding: 50px;
}
.header__aside {
background-color: #cfcfcf;
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
font-size: 1.2rem;
height: 100%;
opacity: 0.9;
position: fixed;
right: -30%;
top: 0;
transition: right 0.5s ease-out;
width: 30%;
z-index: 10;
}
.header__aside--open {
right: 0;
}
<p class='main'>This example</p>
<button type="button" onClick="myFunction2()">
☰
</button>
<div class="header__aside" id="fname">
<div class="aside-nav">
<div class="aside-nav__header">
<button type="button" class="aside-nav__header-button" onClick="myFunction2()">x</button>
</div>
<nav class="nav nav__aside">
<a class="nav__aside-link active" aria-current="true" href="/">Main</a>
</nav>
</div>
</div>
To have Onblur on an element it should receive focus first, Div elements don't receive focus by default. You can add tabindex="0"
<div tabindex="0" ....
Thanks to Emil, for react you can use
tabIndex={0}
You will have to set a tabindex on the div and manually focus it to get the blur handler invoked
var d = document.getElementById("fname");
d.addEventListener("blur", myFunction);
function myFunction() {
alert("Input field lost focus.");
}
var state = true;
function myFunction2() {
state = !state;
d.focus();
if (state) {
d.className = "header__aside";
} else {
d.className += " header__aside--open";
}
}
.main {
background-color: violet;
padding: 50px;
}
.header__aside {
background-color: #cfcfcf;
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
font-size: 1.2rem;
height: 100%;
opacity: 0.9;
position: fixed;
right: -30%;
top: 0;
transition: right 0.5s ease-out;
width: 30%;
z-index: 10;
}
.header__aside--open {
right: 0;
}
<p class='main'>This example</p>
<button type="button" onClick="myFunction2()">
☰
</button>
<div class="header__aside" id="fname" tabindex="0">
<div class="aside-nav">
<div class="aside-nav__header">
<button type="button" class="aside-nav__header-button" onClick="myFunction2()">x</button>
</div>
<nav class="nav nav__aside">
<a class="nav__aside-link active" aria-current="true" href="/">Main</a>
</nav>
</div>
</div>
Add tabindex=0 to the panel-html and focus() to the JS opening the panel:
<div id=panel tabindex=0></div>
document.getElementById('panel').onclick = function(){
document.getElementById('panel').style.display = 'block';
document.getElementById('panel').focus();
document.getElementById('panel').onblur = function(){
document.getElementById('panel').style.display = 'none';
}
}
Related
Im building an app where you can like and dislike movies. I have a database of movies and show each one to the user. When you click like or dislike it takes you to the next poster.
I also have access to the description and rating. I've added my own modal to show the user when clicked more information on that movie.
The problem Im having is the modal works for the first movie and changes the class to active. However on the second one it stops working. I can see in the inspector the modal code is there and has the correct information for that movie. But the modal seems to still not work/become unresponsive. Perhaps its an error that I've missed. I notice that it does add active once again on the first item. Perhaps I need to find a way to switch to the current movie instead or incorporate the modal into the slides more? or find a way to reset it..
EDIT -:
Adding to the above. Having done a bit more testing. It seems like the modal doesnt know what id the slide is on.. So it never knows when the slide is changed.. Any ideas how I would do this?
EDIT2 -:
I've now got it so every modal has a unique id per poster using the contact.id. Any way to link the modal id together with the slides so when I change the slide the modal knows and changes to active on the next slide?
const modalBtn = document.querySelectorAll(".modal-btn");
const modalBg = document.querySelectorAll("movie-modal");
const modalClose = document.querySelectorAll(".close-button");
const overlay = document.querySelectorAll("overlay");
const overlayClose = document.querySelectorAll("overlay");
const activeModal = document.querySelectorAll(".movie-modal:first-child")
modalBtn.forEach(function(btn, index){
btn.addEventListener('click', function(click) {
console.log(click)
modalBg.classList.add('active');
overlay.classList.add('active');
var content_id = activeModal.data("id");
console.log(content_id)
});
});
modalClose.forEach(function(btn, index){
btn.addEventListener('click', function(click) {
console.log(click)
modalBg.classList.remove('active');
overlay.classList.remove('active');
});
});
overlay.forEach(function(overlay, index){
overlay.addEventListener('click', function() {
overlayClose.classList.remove('active');
});
});
overlay.forEach(function(overlay, index){
overlay.addEventListener('click', function() {
const modals = document.querySelectorAll('.movie-modal.active')
modals.forEach(modal => {
modal.classList.remove('active');
overlay.classList.remove('active');
})
})
});
$(function(){
var $activeSlide = $('#slides .slide:first-child');
// show first slide
$activeSlide.addClass("active");
// on click event decline
$("#decline").on("click", function(){
goToSlide('decline');
});
// on click approve then what?
$("#approve").on("click", function(){
var content_id = $activeSlide.data("id");
console.log(content_id)
goToSlide('approve');
// $.ajax({
// url: "/user_contents/liked" + content_id,
// method: "post",
// dataType: "ajax"
// });
});
// adding the 'showing' or 'active' slide class to each element
function goToSlide(action) {
$activeSlide.removeClass("active");
$activeSlide = $activeSlide.next(".slide");
// send data to controller
if(action == "approve"){
console.log(action);
} else {
console.log('dislike');
}
$activeSlide.addClass("active");
}
});
.movie-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
transition: 200ms ease-in-out;
box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
border-radius: 10px;
z-index: 10;
background-color: white;
width: 300px;
max-width: 80%;
color: black;
}
.movie-modal.active{
transform: translate(-50%, -50%) scale(1);
}
.movie-modal-header {
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgb(182, 182, 182);
}
.movie-rating {
font-size: 1.25rem;
font-weight: bold;
}
.close-button {
cursor: pointer;
border: none;
outline: none;
background: none;
font-size: 1.25rem;
font-weight: bold;
}
.movie-modal-body {
padding: 16px;
color: rgb(160, 160, 160);
}
#overlay {
position: fixed;
opacity: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.35);
pointer-events: none;
transition: 200ms ease-in-out;
}
#overlay.active {
opacity: 1;
pointer-events: all;
}
#slides {
position: relative;
height: 100%;
padding: 0px;
margin: 0 auto;
list-style-type: none;
}
.slide {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
opacity: 0;
z-index: 1;
box-sizing: border-box;
color: #fff;
-webkit-transition: opacity 1s;
-moz-transition: opacity 1s;
-o-transition: opacity 1s;
transition: opacity 1s;
}
.showing {
opacity: 1;
z-index: 2;
}
#slide-controls {
margin: 20px 0 400px;
width: 100%;
position: absolute;
display: flex;
justify-content: center;
justify-content: space-between;
z-index: 10;
color: #ffffffba;
font-size: 24px;
top: 50%;
}
.image-carousel {
width: 100%;
height: 642px;
margin: 0 auto;
background-size: contain;
background-repeat: no-repeat;
position: relative;
}
#decline {
margin-left: 16px;
}
#approve {
margin-right: 16px;
}
.img-thumbnail {
padding: 0 !important;
}
<ul id="slides">
<% #contents.each_with_index do |content, i| %>
<li class="slide <%= 'active' if i == 0 %>" data-id="<%= content.id %>">
<div class="image-carousel" style="background-image: url('<%= content.poster %>')">
</div>
<button class="modal-btn">Open Modal</button>
<div id="movie-modal <%= content.id %>" class="movie-modal">
<div class="movie-modal-header">
<div class="movie-rating" style="">Average rating: <strong><%= content.rating %></strong></div>
<button class="close-button">×</button>
</div>
<div class="movie-modal-body"><p><%= content.description %></p></div>
</div>
<div id="overlay <%= content.id %>"></div>
</li>
<% end %>
</ul>
<div id="slide-controls">
<span id="decline"><i class="fa fa-solid fa-thumbs-down fa-2x"></i></span>
<span id="approve"><i class="fa fa-solid fa-thumbs-up fa-2x"></i></span>
</div>
This shows when the first poster is clicked the modal works and movie-modal active is added to the modal class
The second image with Morbius in it.. The modal class doesnt change for the current slide showing however it changes for the previous slide.. BUT the previous slide only changes once and then sticks to active. Nothing is shown on the poster.
I am trying to create a multilayer or nested accordion menu using pure JavaScript. The whole thing is functioning correctly expect for one issue: when I click on one element of the menu, the entire thing reacts.
This is not for a current project. I am simply trying to expand my knowledge after taking a basic course.
I know the easy-but-bloated way to fix this would be to replicate the functions three times and then specifically target each node (is that even the right word?) in the array. However, I image that would be the wrong way to do things. As you’ll see, I attempted to create a loop for multiple variables. I think this might be my issue because it seems to be the thing that is linking all of them together.
var accordion = document.getElementsByClassName("accordion");
var dropdown = document.getElementsByClassName("dropdown");
var accordionArrow = document.getElementsByClassName("accordionArrow");
var dropdownArrow = document.getElementsByClassName("dropdownArrow");
var content = document.getElementsByClassName("content");
function accordionFunction() {
for (j = 0, k = 0, l = 0, m = 0; j < dropdown.length, k < accordionArrow.length, l < dropdownArrow.length, m < content.length; j++, k++, l++, m++) {
if (dropdown[j].style.maxHeight) {
dropdown[j].style.maxHeight = null;
accordionArrow[k].style.transform = null;
content[m].style.maxHeight = null;
dropdownArrow[l].style.transform = null;
} else {
dropdown[j].style.maxHeight = dropdown[j].scrollHeight + "px";
accordionArrow[k].style.transform = "rotate(-135deg)";
}
}
};
for (var i = 0; i < accordion.length; i++) {
accordion[i].addEventListener("click", accordionFunction);
};
function accordionSubmenu() {
for (l = 0, m = 0; l < dropdown.length, m < content.length; l++, m++) {
if (content[m].style.maxHeight) {
content[m].style.maxHeight = null;
dropdownArrow[l].style.transform = null;
} else {
content[m].style.maxHeight = content[m].scrollHeight + "px";
dropdownArrow[l].style.transform = "rotate(-135deg)";
}
}
};
for (j = 0; j < dropdown.length; j++) {
dropdown[j].addEventListener("click", accordionSubmenu)
};
body {
margin: auto;
width: 600px;
}
div {
margin: auto;
}
.accordion {
background-color: lightblue;
color: white;
padding: 3%;
cursor: pointer;
width: 300px;
height: 50px;
}
.accordion .accordionArrow {
border: solid white;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.dropdown {
color: lightblue;
padding-left: 3%;
cursor: pointer;
width: 300px;
max-height: 0;
overflow: hidden;
transition-duration: 0.2s;
}
.dropdown .dropdownArrow {
border: solid lightblue;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.content {
font-weight: bold;
transition: all 0.2s ease;
padding-left: 5%;
max-height: 0;
overflow: hidden;
}
<div>
<h2 class="accordion">Main 1<i class="accordionArrow"></i></h2>
<h3 class="dropdown">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content">Hello there. We are exposed.</p>
</div>
<div>
<h2 class="accordion">Main 2<i class="accordionArrow"></i></h2>
<h3 class="dropdown">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content">Hello there. We are exposed again!</p>
</div>
<div>
<h2 class="accordion">Main 3<i class="accordionArrow"></i></h2>
<h3 class="dropdown">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content">Hello there. We are exposed thrice!</p>
</div>
With some smart use of the this keyword (the element that triggered the event handler) and the nextElementSibling property you can achieve this in a more elegant way.
I made a class to make elements max-height 0. So now i can hide and show elements by just adding or removing this class.
In the accordion function I toggle this class for the element after the clicked element and remove the class for all others.
In the accordion submenu I just toggle the class on the nextElementSibling.
Personally I would make a different html structure where you can toggle classes easier by using more levels of elements so you would only need 1 function for x amount of submenu's. Maybe a nice new challenge for you.
document.querySelectorAll('.accordion').forEach((accordion) => accordion.addEventListener('click', function() {
//Get All possible hidden elements and loop over it.
document.querySelectorAll('.dropdown, .content').forEach((collapsible) => {
//If current element is the same is the next sibling element of the event target toggle the class. Otherwise add it.
if(collapsible === this.nextElementSibling) {
collapsible.classList.toggle('maxHeightZero');
}
else {
collapsible.classList.add('maxHeightZero');
}
});
}));
document.querySelectorAll('.dropdown').forEach((dropdown) => dropdown.addEventListener('click', function() {
//toggle the nextElementSibling
this.nextElementSibling.classList.toggle('maxHeightZero');
}));
body {
margin: auto;
width: 600px;
}
div {
margin: auto;
}
.accordion {
background-color: lightblue;
color: white;
padding: 3%;
cursor: pointer;
width: 300px;
height: 50px;
}
.accordion .accordionArrow {
border: solid white;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.dropdown {
color: lightblue;
padding-left: 3%;
cursor: pointer;
width: 300px;
overflow: hidden;
transition-duration: 0.2s;
}
.dropdown .dropdownArrow {
border: solid lightblue;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.content {
font-weight: bold;
transition: all 0.2s ease;
padding-left: 5%;
overflow: hidden;
}
.maxHeightZero {
max-height: 0;
}
<div>
<h2 class="accordion">Main 1<i class="accordionArrow"></i></h2>
<h3 class="dropdown maxHeightZero">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content maxHeightZero">Hello there. We are exposed.</p>
</div>
<div>
<h2 class="accordion">Main 2<i class="accordionArrow"></i></h2>
<h3 class="dropdown maxHeightZero">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content maxHeightZero">Hello there. We are exposed again!</p>
</div>
<div>
<h2 class="accordion">Main 3<i class="accordionArrow"></i></h2>
<h3 class="dropdown maxHeightZero">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content maxHeightZero">Hello there. We are exposed thrice!</p>
</div>
When I click on my element with an event listener I supposed to get my popup shown, which works just fine until I introduce onClickOutside(). Using ng-click-outside
export class Component {
private isPopupDisplayed: boolean = false;
onClick() {
console.log('Clicked');
this.isPopupDisplayed = true;
}
onClickedOutside(event) {
this.isPopupDisplayed = false;
}
}
.popup{
position: absolute;
z-index: 3;
width: 200px;
height: 94px;
background-color: $secondary-color;
left: 0%;
bottom: 0%;
padding: 15px 0px;
margin: 12px;
border: 1px solid $modal-border-color;
border-radius: 4px;
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.25);
li:hover {
background-color: $accent-color;
}
li {
padding-top: 5px;
height: 32px;
}
}
<div *ngIf="isPopupDisplayed" >
<ul class="popup" (clickOutside)="onClickedOutside($event)">
<li><a>My Details</a></li>
<li><a>Sign out</a></li>
</ul>
</div>
<div class="presenter-menu_avatar" (click)="onClick()" >
<img [src]="avatarUrl">
</div>
When I am trying to introduce onClickOutside() method, I can see that both methods are triggered at the same time as my popup still not there and that automatically triggers not even showing it up.
My aim is to simply show the popup when onClick() and hide it when clicked outside of it.
Add stopPropagation event in click function
onClick(event) {
event.stopPropagation()
console.log('Clicked');
this.isPopupDisplayed = true;
}
<div class="presenter-menu_avatar" (click)="onClick($event)" >
<img [src]="avatarUrl">
</div>
I developed a UI where you will click a card it will pop up but inside that card there are two links on which when I click it should not pop back. I know that those links are part of that card and I put an event on the card selector.
So I want to achieve this scenario:
When user clicks on a card no matter where ... it should pops up.. (Its doing it right now) but when user clicks on those two links it should not pop back ... it should show tabs which I will handle myself ... Just need a way that it should not pop back on clicking on those links because I want to fire another event to display tabs on those links.
I tried it with :not but it didn't work for me.
var card = $('.card');
card.click(function() {
if ($(this).hasClass('gravitate')) {
$(this).removeClass('gravitate');
$(this).addClass('levitate');
} else {
$(this).addClass('gravitate');
$(this).removeClass('levitate');
}
});
* {
padding: 0;
margin: 0;
}
body {
padding: 30px;
}
.card {
border: #ececec 1px solid;
padding: 10px;
width: 200px;
margin-bottom: 10px;
}
.tab-pane {
display: none;
}
.transition {
transition: all 500ms
}
.gravitate {
box-shadow: 0 0 18px 3px rgba(0, 0, 0, 0);
width: 200px;
transform: translate(0, 0);
}
.levitate {
box-shadow: 0 0 18px 3px rgba(0, 0, 0, 0.3);
width: 220px;
transform: translate(-10px, 5px);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="card gravitate transition">
<h4>Sample Card</h4>
First Name | Last Name
<div class="tab-pane first-name">Omer</div>
<div class="tab-pane last-name">Hussain</div>
</div>
<div class="card gravitate transition">
<h4>Sample Card</h4>
First Name | Last Name
<div class="tab-pane first-name">Usman</div>
<div class="tab-pane last-name">Ali</div>
</div>
You can add an event handler to your links to check if the parent card has the levitate class and if so, stop the click event from bubbling up the DOM and triggering your other event handler:
$('a.tab').click(function(e) {
if ($(this).parent().hasClass('levitate')) {
e.stopPropagation();
}
})
var card = $('.card');
card.click(function() {
if ($(this).hasClass('gravitate')) {
$(this).removeClass('gravitate');
$(this).addClass('levitate');
} else {
$(this).addClass('gravitate');
$(this).removeClass('levitate');
}
});
$('a.tab').click(function(e) {
if ($(this).parent().hasClass('levitate')) {
e.stopPropagation();
}
})
* {
padding: 0;
margin: 0;
}
body {
padding: 30px;
}
.card {
border: #ececec 1px solid;
padding: 10px;
width: 200px;
margin-bottom: 10px;
}
.tab-pane {
display: none;
}
.transition {
transition: all 500ms
}
.gravitate {
box-shadow: 0 0 18px 3px rgba(0, 0, 0, 0);
width: 200px;
transform: translate(0, 0);
}
.levitate {
box-shadow: 0 0 18px 3px rgba(0, 0, 0, 0.3);
width: 220px;
transform: translate(-10px, 5px);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="card gravitate transition">
<h4>Sample Card</h4>
First Name | Last Name
<div class="tab-pane" class="first-name">Omer</div>
<div class="tab-pane" class="last-name">Hussain</div>
</div>
<div class="card gravitate transition">
<h4>Sample Card</h4>
First Name | Last Name
<div class="tab-pane" class="first-name">Usman</div>
<div class="tab-pane" class="last-name">Ali</div>
</div>
If I understand it right, you want that any click on the card expands/retracts it, but not if the user clicks any of the links. If so, it's as simple as handling the hyperlinks click function separately and preventing the event to propagate. Just add this to your JavaScript:
var cardTabs = $('.tab');
cardTabs.click(function() {
alert("your handler");
return false;
});
You can also use prevent default and stop propagation methods of the click event, which are equivalent (see: event.preventDefault() vs. return false)
var cardTabs = $('.tab');
cardTabs.click(function(e) {
alert("your handler");
e.preventDefault();
e.stopPropagation();
});
I am trying to create a carousel like effect on divs such thta when i click on a but, the next div displays and the previous one fadeOut. Below is my code
var oCurImage = $(".webTut div.current");
var oNxtImage = $(oCurImage).next();
var leftBtn = $('.tutLeft'), rightBtn = $('.tutRight');
$(rightBtn).click(function() {
oCurImage.fadeOut().removeClass('current');
oNxtImage.fadeIn().addClass('current').animate({opacity: 1.0}, 1000);
if (oNxtImage.length == 0) {
oNxtImage = $(".webTut div:first-child");
}
});
HTML
<div class="webTut">
<span class="tutBtn"><a class="tutLeft"><i class="fa fa-angle-left"></i></a><a class="tutRight"><i class="fa fa-angle-right"></i></a></span>
<div class="current">
<img src="images/egold.png">
<h1>cname</h1>
<h3>Welcome to nigeriaeexport.com</h3>
<p>Your one stop platform for everything export</p>
</div>
<div style="background: #fff;"></div>
<div style="background: #dd0d0d;"></div>
</div>
css
.webTut div {
width: 60%;
height: 80%;
background: #28bc88;
margin: 6.5% auto 0 auto;
border-radius: 4px;
box-shadow: 0 0 4px rgba(0,0,0,.5);
-webkit-box-shadow: 0 0 4px rgba(0,0,0,.5);
text-align: center;
display: none;
/*854 x 720*/
}
.webTut div.current {
display: block;
z-index: 1;
}
The thing is i have used this same method with setInterval for an image slider but here when i click on the button the first time, it shows the next div (the white one) but when i click again, it doesn't change to the next div (the red one #dd0d0d). What could be the issue and how do i fix. Thanks.
Once you click on right anchor tag you should change value of oCurImage and oNxtImage and for getting first div inside div with class .webTut you should use $(".webTut div:first");
You can run below code snippet.
var oCurImage = $(".webTut div.current");
var oNxtImage = $(oCurImage).next();
var leftBtn = $('.tutLeft');
var rightBtn = $('.tutRight');
$(rightBtn).click(function() {
oCurImage.fadeOut().removeClass('current');
if (oNxtImage.length == 0) {
oNxtImage = $(".webTut div:first");
}
oNxtImage.fadeIn().addClass('current').animate({opacity: 1.0}, 1000);
oCurImage = $(".webTut div.current");
oNxtImage = $(oCurImage).next();
});
.webTut div {
width: 60%;
height: 80%;
background: #28bc88;
margin: 6.5% auto 0 auto;
border-radius: 4px;
box-shadow: 0 0 4px rgba(0,0,0,.5);
-webkit-box-shadow: 0 0 4px rgba(0,0,0,.5);
text-align: center;
display: none;
/*854 x 720*/
}
.webTut div.current {
display: block;
z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="webTut">
<span class="tutBtn"><a class="tutLeft">left</a> <br/><br/><a class="tutRight">right</i></a></span>
<div class="current">
<h1>cname</h1>
<h3>Welcome to nigeriaeexport.com</h3>
<p>Your one stop platform for everything export</p>
</div>
<div style="background: #fff;">First Div</div>
<div style="background: #dd0d0d;">Second Div</div>
</div>