I am working on creating a website and I am stuck on a certain function I am trying to build. I am trying to slide back a div to its original place if anyplace outside the div is clicked. I've looked everywhere on stack but to no avail. What happens to me is that the background clicks remain active at all times, I only need it to be active when the div has slid to become sort of a popup.
Here is my jsfiddle: https://jsfiddle.net/DTcHh/10567/
Here is the jquery for one of the divs (the rest are similar)
var text = 1;
$('.login1').click(function(e){
e.preventDefault();
$('.loginform_hidden').toggleClass('loginform_visible');
$(".animateSlide").toggle(300, function(){
$(this).focus();
});
if(text == 1){
$(".div1").toggleClass("animateSlide col-xs-12");
$('.login1').html('Go Back');
$('.imageOne').toggleClass('animateSlideTop');
// If an event gets to the body
$('.div2, .div3, .patientAccess').toggle("fast");
document.addEventListener('mouseup', function(event){
var box = document.getElementsByClassName('animateSlide');
if (event.target != box && event.target.parentNode != box){
$('.div2, .div3, .patientAccess').toggle("fast");
$(".div1").toggleClass("animateSlide ");
text=0;
}
});
text = 0;
} else {
$(".div1").toggleClass("animateSlide");
$('.login1').html('Start Animation');
$('.imageOne').toggleClass('animateSlideTop');
$('.div2, .div3, .patientAccess').toggle("fast");
text = 1;
}
});
$(".div1").on('blur', function() {
$(this).fadeOut(300);
});
EDIT: The jsfiddle now incorporates what I have been trying to utilize.
As a demonstration, I built a simplified version of what I think you're aiming to achieve.
I'm using the "event.target" method described in this answer.
Since you are using CSS transitions, I'm using jQuery to detect the end of those transitions using a method found here.
I've given all boxes a class of "animbox" so that they can all be referenced as a group. I've also given each box its own ID so it can be styled individually with CSS.
I've commented the code in an attempt to explain what's going on.
// define all box elements
var $allBoxes = jQuery('.animbox');
// FUNCTION TO SHOW A SELECTED BOX
function showBox($thisBox) {
$allBoxes.hide(); // hide all boxes
$thisBox.show().addClass('animateSlide'); // show and animate selected box
$('div.login', $thisBox).text("Go Back"); // change the selected box's link text
}
// FUNCTION TO RETURN BOXES TO THE DEFAULT STATE
function restoreDefaultState() {
var $thisBox = jQuery('div.animbox.animateSlide'); // identify an open box
if ($thisBox.length) { // if a box is open...
$thisBox.removeClass('animateSlide'); // close this box
$thisBox.one('webkitTransitionEnd'+
' otransitionend'+
' oTransitionEnd'+
' msTransitionEnd'+
' transitionend', function(e) { // when the box is closed...
$allBoxes.show(); // show all boxes
$('div.login', $thisBox).text("Start Animation"); // change the link text
});
}
}
// CLICK HANDLER FOR ALL "login" TRIGGERS
$('div.login').click(function(e) {
var $thisBox = $(this).closest('div.animbox'); // identify clicked box
if (!$thisBox.hasClass('animateSlide')) { // if the box is not open...
showBox($thisBox); // open it
} else { // otherwise...
restoreDefaultState(); // restore the default state
}
});
// CLICK HANDLER TO RESTORE DEFAULT STATE WHEN CLICK HAPPENS OUTSIDE A BOX
$('body').click(function(evt) {
if ($(evt.target).hasClass('animbox') || // if a box is clicked...
$(evt.target).closest('div.animbox').length > 0) { // or a child of a box...
return; // cancel
}
restoreDefaultState(); // restore the default state
});
div.container-fluid {
background-color: #464646;
}
.v-center {
display: table;
height: 100vh;
}
.content {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.patientAccess {
transition: all .5s;
background: white;
height: 200px;
width: 90%;
position: absolute;
opacity: 0.7;
margin-top: -100px;
}
.patientAccess p {
font-size: 1.5em;
font-weight: bold;
}
div.animbox {
transition: all .5s;
position: absolute;
cursor: pointer;
width: 90%;
height: 100px;
opacity: 0.7;
}
div#animbox1 {
background: #e76700;
}
div#animbox2 {
background: #74b8fe;
}
div#animbox3 {
background: #848484;
}
div.login {
color: white;
font-size: 1em;
cursor: pointer;
}
div#animbox1.animateSlide {
width: 200px;
height: 300px;
margin-left: 100px;
opacity: 1;
}
div#animbox2.animateSlide {
width: 250px;
height: 450px;
margin-left: -25px;
margin-top: -150px;
}
div#animbox3.animateSlide {
width: 150px;
height: 150px;
opacity: .5;
margin-left: -100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<div class="container-fluid">
<div class="row-fluid">
<div class="col-xs-12 v-center">
<div class="content text-center">
<div class="col-xs-2 animated slideInRight "></div>
<div class="col-xs-2 animated slideInRight ">
<div class="patientAccess">
<p>Patient Resource Access</p>
</div>
</div>
<div class="col-xs-2 animated slideInRight">
<div class="animbox" id="animbox1">
<div class="login">Start Animation</div>
<div class="loginform_hidden "></div>
</div>
</div>
<div class="col-xs-2 animated slideInRight">
<div class="animbox" id="animbox2">
<div class="login">Start Animation</div>
<div class="registrationform_hidden"></div>
</div>
</div>
<div class="col-xs-2 animated slideInRight">
<div class="animbox" id="animbox3">
<div class="login">Start Animation</div>
</div>
</div>
</div>
</div>
</div>
</div>
You can namespace an event handler using this syntax:
$("#myElement").on("click.myEventHandlerName", function() { ... });
At any point, you can remove the event handler again by calling
$("#myElement").off("click.myEventHandlerName", "#myElement");
Related
I am trying to make a simple progress bar where the click event changes the class according to my CSS classes:
so what i need to do is determine what the current class is and change the last character of the class so if the current bar is:
and the user clicks on the next button:
the script would be?
$(document).on('click', '.progress-next', function() {
//1. get current step-?
//2. incriment current step + 1
//3. remove current step-? from .progress-bar (once i know how to handle getting the classes i have this part :)
//4. add new incremented class to .progress-bar (once i know how to handle getting the classes i have this part :)
});
.progress-bar {
&.step-1 {
width: 25%;
}
&.step-2 {
width: 50%;
}
&.step-3 {
width: 75%;
}
&.step-4 {
width: 100%;
}
}
<div class="progress">
<div class="progress-bar progress-bar-striped step-1 active">Start</div>
</div>
<button class="btn btn-default progress-next">Next</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Since the fourth step doesn't need incrementing, it's only 3 steps that you really need to check. So you could simply check for those 3 classes with $('.progress-bar').hasClass('step-1'), $('.progress-bar').hasClass('step-2') and $('.progress-bar').hasClass('step-3').
Or make a little loop to shorten the code:
$(document).on('click', '.progress-next', function() {
var $progressbar = $('.progress-bar');
for (var i = 1; i<4; i++) {
var className = 'step-'+i;
if ($progressbar.hasClass(className)) {
$progressbar.removeClass(className).addClass('step-'+(i+1));
break;
}
}
});
.progress-bar {
background-color: green;
}
.progress-bar.step-1 {
width: 25%;
}
.progress-bar.step-2 {
width: 50%;
}
.progress-bar.step-3 {
width: 75%;
}
.progress-bar.step-4 {
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress">
<div class="progress-bar progress-bar-striped step-1 active">Start</div>
</div>
<button class="btn btn-default progress-next">Next</button>
Try this, it should be what you are looking for.
I left your steps so you can follow the code and what it does.
$(document).on('click', '.progress-next', function() {
//1. get current step-?
var cl = $(".progress-bar").attr("class").split(/\s/).filter(function( cn ) {
return cn.indexOf('step') === 0;
});
//console.log(cl)
//2. incriment current step + 1
var step = parseInt(cl[0].split('-')[1]) + 1;
//console.log(step)
//3. remove current step-? from .progress-bar (once i know how to handle getting the classes i have this part :)
var newclass = "step-" + step;
//console.log(newclass)
//4. add new incremented class to .progress-bar (once i know how to handle getting the classes i have this part :)
$(".progress-bar").removeClass(cl[0]).addClass(newclass)
})
.progress-bar {
background-color: blue;
}
.progress-bar.step-1 {
width: 25%;
}
.progress-bar.step-2 {
width: 50%;
}
.progress-bar.step-3 {
width: 75%;
}
.progress-bar.step-4 {
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress">
<div class="progress-bar progress-bar-striped step-1 active">Start</div>
</div>
<button class="btn btn-default progress-next">Next</button>
Have a look into that snippet.
I changed something in your CSS, because i didn't know what the & signs meant.
I also added a variable current which got the current state of your bar.
//init the state of the bar
var current = 1;
$('.progress-bar').addClass('step-' + current)
//function to increase by buttonclick
$(document).on('click', '.progress-next', function() {
$('.progress-bar').removeClass('step-' + current)
current = current + 1
$('.progress-bar').addClass('step-' + current)
})
.progress-bar {
background: red;
}
.step-1 {
width: 25%;
}
.step-2 {
width: 50%;
}
.step-3 {
width: 75%;
}
.step-4 {
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress">
<div class="progress-bar">Start</div>
</div>
<button class="btn btn-default progress-next">Next</button>
I am still fairly new to JS, and I am trying to replace the HTML of a div with a picture that is being moused over, and when the mouse leaves I want it to return to it's normal state. I thought that I did everything right but my code doesn't seem to be working. I've looked through stack overflow and I see a lot of jQuery solutions to my 'problem,' but I would like an answer in pure JavaScript (I'm trying to "maser" this first), along with an explanation so I can understand why the answer IS the answer. Thanks.
I'll try to explain myself (my code). I grabbed reference to the image holder, and I grabbed reference to the the images. I thought I made a function that looped through the array of images and added an event listener to whichever image ( image[i] ) was being moused over. Then, I added an event listener that is supposed to return the image holder to it's default state by inserting the original HTML. I just don't understand how to fix this.
var holder = document.getElementById('holder');
var images = document.getElementsByTagName('img');
var popImage = function () {
for (i = 0; i < images.length; i++) {
images[i].addEventListener('mouseover', = function () {
holder.innerHTML = images[i];
});
images[i].addEventListener('mouseout', function () {
holder.innerHTML =
'<div class='col-md-3 img-fluid' id='img1'><img src='photo1.jpg'></div>
<div class='col-md-3 img-fluid' id='img2'><img src='photo2.jpg'></div>
<div class='col-md-3 img-fluid' id='img3'><img src='photo3.2.jpg'></div>
<div class='col-md-3 img-fluid' id='img4'><img src='photo4.jpg'></div>'
});
};
};
popImage();
You said you are new to JS and just learning which is great but an important part of learning JS is learning when not to use it. As #Yoda said if this was for production you really should use CSS instead of JS.
Here is one way you could accomplish this with pure CSS
<style>
.img {
width: 100px;
height: 100px;
background: #bada55;
border: 2px solid #333;
float: left;
}
.holder:hover > .img {
opacity: 0;
}
.holder:hover > .img:hover {
opacity: 1;
}
</style>
<div class="holder">
<!-- Using div.img for simplicity, these whould be your <img/> tags -->
<div class="img">1</div>
<div class="img">2</div>
<div class="img">3</div>
<div class="img">4</div>
</div>
For the purpose of learning, here's how you'd do it in JS:
var holder = document.getElementById('holder');
var images = document.querySelectorAll('.img');
var filter = false;
function popImage () {
// Use for (var i = 0 . . .
// Instead of for (i = 0 . . .
// Because without var, i will be stored in the global scope
for (var i = 0; i < images.length; i++) {
(function (_i) {
images[_i].addEventListener('mouseover', function () {
holder.innerHTML = '';
// We can't set innerHTML to images[_i]
// because it's a DomNode not a string
holder.appendChild(images[_i]);
});
})(i);
}
holder.addEventListener('mouseout', function (e) {
if (e.target !== holder)
return;
holder.innerHTML = '';
// Again, use var j = 0 . . .
for (var j = 0; j < images.length; j++) {
holder.appendChild(images[j]);
}
});
}
popImage();
.img {
width: 100px;
height: 100px;
background: #bada55;
border: 2px solid #333;
display: inline-block;
}
#holder {
position: relative;
width: 100%;// So doesn't collape and trigger mouseout
height: 100px;
background: red;
padding: 20px 0;
}
<div id="holder">
<!-- Again, these would be your image tags -->
<div class="img">1</div>
<div class="img">2</div>
<div class="img">3</div>
<div class="img">4</div>
</div>
I had 10 mins before leaving work so I had a crack at this to see how I would do it and give you some ideas.
Here is my implementation (https://jsfiddle.net/hg7s1pyh/)
I guess the main thing here is that I've broken it down into lots of smaller parts, this makes solving problems far easier, each method is concerned with doing one thing only.
You will also note the use of classes to show and hide content rather than removing it entirely, this takes lots of the arduous work out of this feature.
function attachEvents() {
var images = getImages();
images.forEach(function(image) {
attachMouseOverEvent(image);
attachMouseLeaveEvent(image);
});
}
function attachMouseOverEvent(element) {
element.addEventListener('mouseover', function(e) {
var clonedImage = e.target.cloneNode();
addImageToPreview(clonedImage);
});
}
function attachMouseLeaveEvent(element) {
element.addEventListener('mouseleave', function(e) {
removeImageFromPreview();
});
}
function getImages() {
return document.querySelectorAll('.js-image');
}
function getImagePreviewElement() {
return document.querySelector('.js-image-box');
}
function addImageToPreview(imageElement) {
var previewElement = getImagePreviewElement();
previewElement.classList.add('previewing');
previewElement.appendChild(imageElement);
}
function removeImageFromPreview() {
var previewElement = getImagePreviewElement();
previewElement.classList.remove('previewing');
var image = previewElement.querySelector('.js-image');
image.remove();
}
attachEvents();
.image-box {
position: relative;
min-height: 400px;
width: 400px;
border: 1px solid #000;
text-align: center;
}
.image-box .placeholder {
position: absolute;
top: 50%;
text-align: center;
transform: translateY(-50%);
width: 100%;
}
.image-box.previewing .placeholder {
display: none;
}
.image-box .image {
position: absolute;
top: 50%;
text-align: center;
transform: translate(-50%, -50%);
height: 100%;
width: 100%;
}
.images {
margin-top: 10px;
}
<div class="js-image-box image-box">
<div class="placeholder">
Placeholder
</div>
</div>
<div class="images">
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/350x150"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/150x150"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/400x400"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/350x150"></div>
</div>
I have a click event thats firing. It's working great and does what I need it to do. Here's the problem
The nature of the widget i'm building stacks elements on top of each other through position: absolute When i click on one of these stacked elements, only one event is firing, but id like every element to fire that is under the mouse cursor of the click. Is there a way to do this?
Please check the demo or run the code snippet in full page and click through all the divs to see the result message.
DEMO:
http://plnkr.co/edit/KRWvLmRhGbO200pFkOxL?p=preview
What I am doing here is :
Hide the top element
and
get the next absolute element's co-ordinate with document.elementFromPoint and then repeat.
Stack Snippet:
jQuery(document).ready(function(){
$common = $("div.common").on('click.passThrough', function (e, ee) {
var $element = $(this).hide();
try {
if (!ee) $("#output").empty();
$("<div/>").append('You have clicked on: '+$element.text()).appendTo($("#output"));
ee = ee || {
pageX: e.pageX,
pageY: e.pageY
};
var next = document.elementFromPoint(ee.pageX, ee.pageY);
next = (next.nodeType == 3) ? next.parentNode : next //Opera
$(next).trigger('click.passThrough', ee);
} catch (err) {
console.log("click.passThrough failed: " + err.message);
} finally {
$element.show();
}
});
$common.css({'backgroundColor':'rgba(0,0,0,0.2)'});
});
#output {
margin-bottom: 30px;
}
.common {
position: absolute;
height: 300px;
width: 300px;
padding: 3px;
border: 1px #000 solid;
}
.elem5 {
top: 150px;
left: 150px;
}
.elem4 {
top: 180px;
left: 180px;
}
.elem3 {
top: 210px;
left: 210px;
}
.elem2 {
top: 240px;
left: 240px;
}
.elem1 {
top: 270px;
left: 270px;
}
<script data-require="jquery#3.0.0" data-semver="3.0.0" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
<div id="output"></div>
<div class="common elem1">Top Most Element</div>
<div class="common elem2">Element 2</div>
<div class="common elem3">Element 3</div>
<div class="common elem4">Element 4</div>
<div class="common elem5">Bottom Element</div>
Credit for source:
http://jsfiddle.net/E9zTs/2/
You can use customEvent property
Place all div in a parent div
add a click handler to the parent div
if there is a click in the parent box..determine whether the click is in any of the child boxes
If true. then send a click event to all child box
snippet
//This function changes the color of all child divs
function changeColor(e) {
this.style.background = "red";
}
//this function is attached to the parent div which will send that click event to all divs
function trigger(e) {
//create an event
event = new CustomEvent('click');
//if the event originates from a child div
if (e.target.className == 'box')
//loop through all child div
for (var i = 0; i < all_box.length; ++i) {
//dispatch a click event to each child div
all_box[i].dispatchEvent(event);
}
}
document.getElementById('parent').addEventListener('click', trigger)
var all_box = document.getElementsByClassName('box');
for (var i = 0; i < all_box.length; ++i) {
all_box[i].addEventListener('click', changeColor)
}
.box {
padding: 10px;
display: inline-block;
border: solid black;
}
#parent {
border: solid black;
padding: 10px;
}
;
<div id="parent">
<div class="box" id="primary">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
<div class="box">box3</div>
</div>
So I am modifying a web page and there is a table on the bottom of the page that starts minimized. When you click the arrow it opens upward to reveal the table. I am attempting to modify it so that it already starts off opened when the page loads.
HTML Snippet:
<div class="row" id="cp-toggle">
<div class="col-sm-12">
<div class="col-sm-2 col-sm-offset-5 toggle-button">
<a><span class="glyphicon glyphicon-chevron-up"></span></a>
</div>
<div class="col-sm-12" style="height: calc(100% - 25px);max-height: 250px;background-color:#d3d3d3;">
<div style="height: 100%;max-height: 250px;">
<div style="height: 25px;padding-top: 4px;">
<div style="float: left;padding-right: 9px;">
<span> Posts: </span> <span id="posts_count"></span>
</div>
</div>
<div style="overflow-y: scroll;height: 100%;max-height: 225px;">
<table id="result_table" class="table" style="display:table;" >
<thead class="result_thead"></thead>
<tbody class="result_tbody"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
Javascript:
var control_panel= (function(){
var container = $('#cp-toggle div:first-child');
var btn = $('#cp-toggle div:first-child').find("div").first();
var table_panel = $('#cp-toggle div:first-child div:nth-child(2)').first();
var open_css = "glyphicon-chevron-up";
var close_css = "glyphicon-chevron-down";
var open = function(){
container.find("span").first().switchClass(open_css, close_css);
var h = table_panel.height() + 25;
container.css("top", "calc(100% - "+ h +"px)");
};
var close = function(){
container.find("span").first().switchClass(close_css, open_css);
container.css("top", "calc(100% - 25px)")
};
var isOpen = function(){
return _.contains(container.find("span").first().attr('class').split(/\s+/), close_css);
};
var toggle = function(){
if (isOpen()){
close();
} else {
open();
}
};
btn.on('click', toggle);
return {
open: open,
close: close,
toggle: toggle,
isOpen : isOpen
};
}());
CSS Snippet:
#cp-toggle > div:first-child {
top: calc(100% - 25px);
position: fixed;
z-index: 25;
}
.toggle-button {
height: 25px;
padding-top: 3px;
background-color: #d3d3d3;
border-top-right-radius: 7px;
border-top-left-radius: 7px;
text-align: center;
cursor: pointer;
}
#cp-toggle a {
color: #111;
}
#cp-toggle a:hover {
color: #777;
}
.tab-pane { height: 100%;}
#email-body { height: calc(100% - 80px); }
.body-view { height: 100%; overflow-y: scroll; }
.marked {
color: #ffd700;
}
.marked:hover {
color: #ffd700;
}
I have tried modifying the javascript to call control_panel.open(); at the end. I have tried altering the toggle to start with open();. None of these seem to have any effect on the code. I am not sure if I am looking in the correct area or if I am doing something incorrectly.
Try this (you tried something similar in a comment, but I'll explain in a minute...):
<script>
window.addEventListener('load', function() {
control_panel.open();
});
</script>
The problem with your original attempt...
<script>onLoad=control_panel.open();</script>
... was that it was setting a variable called 'onLoad' with the value of whatever was returned by running the function control_panel.open(), which it did immediately instead of waiting until the page was loaded. Instead, in my example I'm setting an 'onload' listener on the window, so that when the window finishes loading, then it'll run the control_panel.open() function that it is now aware of.
In an cordova/ionic app there is a parent-div on which on-hold-listener is attached and a child-div which subscribed for on-tab events like so:
<div on-hold="doSomething()">
<div on-tap="doSomething2()">...</div>
</div>
From time to time it works but there have been situations in what on-tab was executed instead of on-hold when pressing time was bigger than 500ms.
Might this be done in a better way? Please take into consideration that child-div fills out parent completely and it should remain so.
Thanks in advance.
When you have a div parent of another div the events are propagated.
You can try by yourself here:
.c2 {
width: 200px;
height: 200px;
background-color: red;
}
.c1 {
width: 220px;
height: 220px;
background-color: yellow;
}
<div class="c1" onclick="alert('squareParent');">
<div class="c2" onclick="alert('squareChild');">
</div>
</div>
To avoid this you need to stop the propagation:
document.getElementById("c1").addEventListener('click', function(e) {
alert("c1");
}, false);
document.getElementById("c2").addEventListener('click', function(e) {
alert("c2");
e.stopPropagation();
}, false);
#c2 {
width: 200px;
height: 200px;
background-color: red;
}
#c1 {
width: 220px;
height: 220px;
background-color: yellow;
}
<div id="c1">
<div id="c2">
</div>
</div>
You could check more about javascript bubble if you want more information.
After experimenting with stopPropagation I came up with following answer that needs setTimeout to check for mouse/cursor is being holded.
When just clicking on the child-div(red) doSomething2 is alerted whereas holding onto child-div alerts doSomething of parent instead:
var holding = false;
var secondFunctionCall = false;
document.getElementById("c1").addEventListener('mousedown', function(e) {
holding = true;
setTimeout(function(){
if(holding){
secondFunctionCall = false;
alert("calling doSomething()");
}
},500);
}, false);
document.getElementById("c2").addEventListener('mousedown', function(e) {
holding = true;
secondFunctionCall = true;
}, false);
document.getElementById("c2").addEventListener('mouseup', function(e) {
holding = false;
if(secondFunctionCall){
alert("calling doSomething2()");
}
}, false);
#c2 {
width: 200px;
height: 200px;
background-color: red;
}
#c1 {
width: 220px;
height: 220px;
background-color: yellow;
}
<div id="c1">
<div id="c2">
</div>
</div>
When transfering this code into a cordova-app mouse-event types have to be replaced by touch-event types as it is answered here.