I am trying to create a box that expands and collapses using regular JavaScript (No jQuery). The problem I'm running into is detecting how to properly detect dynamically created elements or classes that are added to elements after pageload.
Here's an example JS fiddle page:
http://jsfiddle.net/1a518a4t/3/
As you can see, it works when you collapse and then expand once, but then it won't collapse again.
JS code:
function test() {
var badge = document.getElementById('test');
var close_button = document.querySelector('.test-close');
close_button.addEventListener("click", close_box);
function close_box() {
badge.style.bottom = '-70px';
close_button.classList.add("test-open");
close_button.classList.remove("test-close");
var open_button = document.querySelector('.test-open');
open_button.addEventListener("click", open_box);
}
function open_box() {
badge.style.bottom = '0';
close_button.classList.remove("test-open");
close_button.classList.add("test-close");
}
}
window.onload = test;
I think I really just want to learn how to replicate jQuery's on method in JavaScript. That works for elements that are dynamically created after pageload.
Use a single event listener. And don't modify inline styles, just switch classes:
var badge = document.getElementById('test');
var button = document.querySelector('.button');
button.addEventListener("click", function toggle_box() {
badge.classList.toggle('opened');
badge.classList.toggle('closed');
});
#test {
width: 300px;
height: 100px;
background-color: #ccc;
position: fixed;
bottom: 0;
right: 20px;
transition: all 0.2s;
}
#test.closed {
bottom: -70px;
}
#test > .button {
position: absolute;
top: 10px;
right: 10px;
width: 10px;
height: 10px;
text-indent: -9999px;
cursor: pointer;
background-color: #000;
}
#test.closed > .button {
background-color: #CE312F;
}
<div id="test" class="opened">
<div class="button">Test</div>
</div>
As mentioned in the comments, this is because the created element is added dynamically and as such you need to delegate so event handler can bind it to the created element. To do that you can look at #Ramswaroop solution to do this in native JavaScript. Although I don't think it's even nessisary to change class and re-bind the different functions. Simply use the same <div> and have a toggle function:
var button = document.querySelector('#test div');
button.addEventListener("click", toggle_box);
...
function toggle_box() {
if(badge.style.bottom == '-70px') {
badge.style.bottom = '-0';
toggleClass("test-close", "test-open");
} else {
badge.style.bottom = '-70px';
toggleClass("test-open", "test-close");
}
}
Fiddle Example
Related
Here is the pen I've created.
HTML
<div class = 'cc'>
<div class = 'bb'><div class = 'aa'> Some word </div></div>
</div>
CSS
.cc {
width: 100%;
min-height: 90px;
margin: 0;
padding: 0;
border: 1px solid #999999;
border-radius: 3px;
padding-left: 20px;
padding-right: 20px;
font-family: "Calibri";
font-size: 17px;
color: #666666;
background-color: rgba(0,0,0,.0);
}
.bb {
height: 100px;
width: 100%;
background-color: rgba(0,0,0,.5);
}
.aa {
position: relative;
top: 50%;
transform: translateY(-50%);
}
Now I want to create a clickable event such that when user click on class bb, page will check the top parameter of class aa - if it is 50% then smoothly change that to 10% and vice versa.
I want to use JavaScript code to achieve that. How can I do that?
hey just tried to gave shot at it , seems its working please look into this
let bb = document.querySelector('.bb');
let aa = document.querySelector('.aa');
bb.addEventListener('click',e => {
let top = window.getComputedStyle(aa).getPropertyValue('top');
if(top === '50px'){
aa.style.top = '10%';
}else{
aa.style.top = '50%';
}
})
Got it. It is tested and it seems to work.
let bb = document.querySelector('.bb');
let aa = document.querySelector('.aa');
bb.addEventListener('click', function(){
if(window.getComputedStyle(aa).getPropertyValue('top') === '50px'){
aa.style.top = '10%';
}else{
aa.style.top = '50%';
}
})
First, I used querySelector to get .bb and .aa.
Then, I added a event listener to bb.
Next, in the event listener I used window.getComputedStyle(), got the value of top from it and checked if it is 50px.
Last of all, if it is, change that to 10%, else change it to 50%.
I did this on CodePen, you can check it here (notice I changed the style from gray to white because gray is hard to read inside a black box).
Is it possible to create a minimalist javasript only on-scroll function to hide my menu bar, so only the menu button shows and the button itself gains a white backgroud colour? I have been looking into this and I believed to have the code fairly down. But I am very new to javasript and cannot fully understand the syntax of it yet. Below is what I have now in a jsfiddle:
https://jsfiddle.net/AngusBerry/zLt0yLou/2/#&togetherjs=Vsth32pa6L
html:
<!DOCTYPE html>
<body>
<header>
<h1><span id="tstscroll">0</span></h1>
<div class="MenuButton" id="mobMenu"></div>
<!--<p>as you can see, this is the header for the website. Here will also be contained all of the links to anywhere on the support system. this and the footer will both be FIXED and will move with the page.</p>-->
</header>
</body>
css:
header {
top: 0px;
position: fixed;
max-height: 100px;
width: 100%;
padding-top: 2px;
padding-bottom: 3.5px;
color: green;
animation: max-height-header;
animation-duration: 1.5s;
}
header h1 {
position: relative;
float: left;
margin-left: 3px;
}
header .MenuButton {
width: 28px;
height: 6px;
border-top: 6px solid;
border-bottom: 18px double;
margin-right: 5px;
margin-top: 2px;
}
javascript:
var mobilemenu = document.getElementById('mobMenu');
var testscroller = document.getElementById('tstscroll');
var x = 0;
document.mobilemenu.addEventListener("scroll", menuScrolMob);
function menuScrolMob(mobilemenu.onscroll) {
testscroller.innerhtml = x += 1;
}
You'll need to run that script either last in your body, or after page been loaded, or else it won't be able to access the elements.
Also, your script code is wrong, so here is a solution showing how to solve both those issues
(function(w, d) { /* this is a closure and will keep its variables
from polluting the global namespace and it also
declare 2 variables (w, d) to be used inside it */
w.addEventListener("load", function() {
var mobilemenu = d.getElementById('mobMenu');
var testscroller = d.getElementById('tstscroll');
var x = 0;
mobilemenu.addEventListener("scroll", function() {
testscroller.innerhtml = x += 1;
});
});
}(window, document)); /* here I pass the window and document object into the
closure's variables (w, d) to make the code slimmer */
This probably cannot be done, but I have a fixed-position div on top of inline html in the page body. The inline html has clickable elements, and the fixed div has a hover event.
The fixed element is an empty div, so it is invisible.
Currently, the fixed element is blocking click events on the item under it.
Is it possible?
This solution is too complicated
https://stackoverflow.com/a/9616491/209942
Possible solution?
https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
Thx
The fixed element should not be prevent the clicks from the item under it unless you are stopping the event propagation.
See this fiddle: https://jsfiddle.net/pv0mygz5/
-- it demonstrates that without event.stopPropagation the event should be intercepted by the listener on the span element.
$('#click-me').on('click', function (e) {
console.log('click triggered');
});
$('.box').on('mouseover', function (e) {
//don't stop event from bubbling
console.log('hover triggered');
});
Could you also include a code snippet that demonstrates your problem?
although IE10 doesn't support it you can use
pointer-events: none;
http://jsfiddle.net/leaverou/XxkSC/light/
In this fiddle you can see a drop down being covered with other elements, the other elements has pointer-events: none so you can click on the arrow down button and the click actually goes to the select element itself.
BR,
Saar
You can also try using z-index. Depending on your layout it may not be a solution, but if your front div is invisible, then it shouldn't create unwanted effect. Like this for example:
document.querySelector('#under').addEventListener('click', function(e) {
e.target.style.color = "blue";
});
document.querySelector('#notunder').addEventListener('click', function(e) {
e.target.style.color = "blue";
});
#fix {
width: 60px;
height: 200px;
position: fixed;
z-index: -1;
top: 0px;
border: 1px solid black;
}
#under {
display: inline;
}
#fixnozindex {
width: 100px;
height: 200px;
position: fixed;
left: 75px;
top: 0px;
border: 1px solid black;
}
#notunder {
display: inline;
}
<div id="fix"></div>
<div id="under">Clickable</div>
<div id="fixnozindex"></div>
<div id="notunder">Not clickable</div>
: )
So, I'm trying to solve a hover effect issue. I have tooltips on some of my links. Code looks like this:
<a href="https://wikipedia.org/wiki/Space_Shuttle_Atlantis">
<h6 class="has-tip">Space Shuttle
<p class="tip">The space shuttle was invented by Santa Claus</p>
</h6>
</a>
And the CSS is a bit more involved:
.tip {
display: block;
position: absolute;
bottom: 100%;
left: 0;
width: 100%;
pointer-events: none;
padding: 20px;
margin-bottom: 15px;
color: #fff;
opacity: 0;
background: rgba(255,255,255,.8);
color: coal;
font-family: 'Ubuntu Light';
font-size: 1em;
font-weight: normal;
text-align: left;
text-shadow: none;
border-radius: .2em;
transform: translateY(10px);
transition: all .25s ease-out;
box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
}
.tip::before {
content: " ";
display: block;
position: absolute;
bottom: -20px;
left: 0;
height: 20px;
width: 100%;
}
.tip::after { /* the lil triangle */
content: " ";
position: absolute;
bottom: -10px;
left: 50%;
height: 0;
width: 0;
margin-left: -13px;
border-left: solid transparent 10px;
border-right: solid transparent 10px;
border-top: solid rgba(255,255,255,.8) 10px;
}
.has-tip:hover .tip {
opacity 1;
pointer-events auto;
transform translateY(0px);
}
Now, on desktop this works wonderfully. You hover over the tiny title and you get a pretty looking tooltip, then if you click anywhere on the title or tooltip (unless you decide to put yet another link in the paragraph which works separately and nicely) you activate the link. Yay : )
Now on mobile, the whole thing gets funky. Touching just activates the link. If you have slow internet, or iOS, you might glimpse the tooltip just as the next page loads.
I would like the following behavior:
User taps on tiny title (h6) which has class (has-tip)
If this is the first tap, the tooltip shows, and nothing else happens. 3)
If the tooltip is already showing when they tap (as in a subsequent
tap) then the link is activate and the new page loads.
Any ideas how I might implement this? No jQuery if possible.
One way to do it is to save a reference to the last clicked has-tip link and to apply a class to it which forces the tip to show. When you click on a link and it matches the the last one clicked, you let the event pass.
EDIT: oh, I forgot to mention you might need a classList shim for old IE.
JSFiddle link.
HTML
<a href="http://jsfiddle.net/1tc52muq/5/" class="has-tip">
JSFiddle<span class="tip">Click for some fun recursion</span>
</a><br />
<a href="http://google.com" class="has-tip">
Google<span class="tip">Click to look for answers</span>
</a>
JS
lastTip = null;
if(mobile) {
var withtip = document.querySelectorAll(".has-tip");
for(var i=0; i<withtip.length; ++i) {
withtip[i].addEventListener("click", function(e) {
if(lastTip != e.target) {
e.preventDefault();
if(lastTip) lastTip.classList.remove("force-tip");
lastTip = e.target;
lastTip.classList.add("force-tip");
}
});
}
}
CSS
.has-tip {
position: abolute;
}
.tip {
display: none;
position: relative;
left: 20px;
background: black;
color: white;
}
.has-tip:hover .tip, .force-tip .tip {
display: inline-block;
}
Edit: Just wanted to say that Jacques' approach is similar, but much more elegant.
On touch devices, you'll need to make a click/tap counter:
1) On first tap of any link, store the link and display the hover state.
2) On another tap, check to see if it's the same as the first, and then perform the normal tap action if it is. Otherwise, clear any existing hovers, and set the new tap target as the one to count.
3) Reset / clear any hovers if you tap on non-links.
I've made a rudimentary JSFiddle that console.logs these actions. Since we're not using jQuery, I didn't bother with adding/removing CSS classes on the elements.
Also, sorry about not writing taps instead of clicks.
var clickTarget;
var touchDevice = true;
if(touchDevice) {
var links = document.getElementsByTagName('a');
for(var i=0; i<links.length; i++) {
links[i].onclick = function(event) {
event.stopPropagation();
event.preventDefault(); // this is key to ignore the first tap
checkClick(event);
};
};
document.onclick = function() {
clearClicks();
};
}
var checkClick = function(event) {
if(clickTarget === event.target) {
// since we're prevent default, we need to manually trigger an action here.
console.log("Show click state and also perform normal click action.");
clearClicks();
} else {
console.log("New link clicked / Show hover");
clickTarget = event.target;
}
}
var clearClicks = function() {
console.log("Clearing clicks");
clickTarget = undefined;
};
http://jsfiddle.net/doydLt6v/1/
Looking to achieve a dimmed-background effect by dimming (or changing the opacity of) all elements on the page but one; I've been trying out :not() as well as some jQuery selectors to try and exclude all but the element, but to no avail. Does anyone know of the best way to do this with SASS/Compass or, failing that, jQuery?
So far, I've tried things like:
.opacityFade:not(.classToExclude) {
STYLES HERE
}
or
$('controlElement').click(function(){
$(body).not('desiredTargetToExclude').toggleClass('classThatFadesStuffOut');
});
Ideally, I'd like to avoid having to write more JS and better separate responsibilities,but there might not be a way to do that. Little new to Front-End development, so I'm not aware of a best way to go about doing this; thanks!!
You can achieve this by placing a blanket over all elements, and then pulling the element you want to display out of the DOM order with the z-index property
.item {
background: #f00;
width: 100px;
height: 100px;
display: inline-block;
margin: 10px;
}
.item.selected {
position: relative;
z-index: 200
}
.blanket {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: black;
opacity: 0.5;
z-index: 100;
}
Note that the element needs to have a non static position.
See http://jsfiddle.net/cyberdash/fCMaT/
you could add another class to the non-dimmed/active div. I'll put together a fiddle.
http://jsfiddle.net/nqT7V/
In Jquery:
$(".item").click(function(){
var $this = $(this);
$this.parent().addClass("dimmed");
$this.parent().find(".item").removeClass("active");
$this.addClass("active");
});
$(".holder").click(function(e){
if (e.target != this) return;
var $this = $(this);
if ($this.hasClass("dimmed")){
$this
.removeClass("dimmed")
.find(".item").removeClass("active");
}
});