So I have this drop down menu that is supposed to have slightly delayed drop on hover, but it doesn't drop at all.
My HTML:
<div class="container">
<ul class="menu">
<li class="overflow-hidden">one
<div class="submenu">test</div>
</li>
<li class="overflow-hidden">two</li>
<li class="overflow-hidden">three</li>
</ul>
</div>
CSS
.menu {
display: block;
width: 100%;
margin: 0;
padding: 0;
}
.overflow-hidden {
display: inline-block;
width: 33%;
height: 20px;
text-align: center;
overflow: hidden;
cursor: pointer;
}
.submenu {
display: block;
height: 200px;
background-color: #999;
}
.container {
width: 100%;
}
What have I missed?
There are a few things you can do to improve your code and make it work:
Document Ready event. you should initialize your code after the DOM has rendered, otherwise your code may be trying to attach events to things that aren't there yet!
$(document).ready(function(){
menuHover();
$('.submenu').width(menuWidth);
});
Scope. Referring to $(this) inside the timer object will not be referring to what you think! Define the element you want to refer to at the top of your function, and you can then safely use this explicit definition in any functions defined within the same scope, and you won't have to worry about their own 'this' being something different.
function () {
var $listItem = $(this);
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(function () {
$listItem.css('overflow', 'visible');
}, 200);
}
Semantics. Naming your list items overflow-hidden is semantically bad practice (that is a style not a name!) ... especially when the item is in the overflow-visible state!. It would be advisable to probably remove this altogether and target your list items by something like .menu li or giving them their own class eg. menu-item.
var menuWidth = $('.container').width();
var menuHover = function () {
var timer;
$(".menu li").hover(
function () {
var $listItem = $(this);
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(function () {
$listItem.css('overflow', 'visible');
}, 200);
},
function () {
var $listItem = $(this);
clearTimeout(timer);
timer = null;
$listItem.css('overflow', 'hidden');
});};
$(document).ready(function(){
menuHover();
$('.submenu').width(menuWidth);
});
You got two things totally wrong.
First: You're missing the jQuery ready event1.
Second: You didn't think about the scope2. $(this) is not available in setTimeout();
$(function(){ // (1a) jQuery ready start
var menuWidth = $('.container').width();
var menuHover = function(){
var timer;
$(".overflow-hidden").hover(
function(){
if(timer){
clearTimeout(timer);
timer = null;
}
var temporary_element = $(this); // (2a) Store element in temporary variable
timer = setTimeout(function(){
temporary_element.css('overflow', 'visible'); // (2b) Get the stored element from the parent scope
}, 200);
},
function(){
clearTimeout(timer);
timer = null;
$(this).css('overflow', 'hidden');
}
);
};
menuHover();
$('.submenu').width(menuWidth);
}); // (1b) jQuery ready end
CSS3 Transitions
With the use of transition (-webkit,-moz,-ms) you don't even need javascript.
You can use the class or id to control sub elements.
CSS
#menu{
}
#menu>div{
width:33%;
float:left;
height:20px;
background-color:grey;
}
#menu>div>div:nth-child(1){
line-height:20px;
text-align:center;
}
#menu>div>div:nth-child(2){
overflow:hidden;
height:0px;
transition:height 700ms ease 500ms;
/*
the duration is 700milliseconds and the delay is 500milliseconds
*/
background-color:#cc2;
padding:0 16px;
}
#menu>div:hover>div:nth-child(2){
height:20px;
}
HTML
<div id="menu">
<div><div>one</div><div>1</div></div>
<div><div>two</div><div>2</div></div>
<div><div>three</div><div>3</div></div>
</div>
DEMO
http://jsfiddle.net/95wM2/
.menu {
display: block;
width: 100%;
margin: 0;
padding: 0;
background-color: green;
}
.overflow-hidden {
display: inline-block;
width: 33%;
height: 20px;
text-align: center;
position: relative;
cursor: pointer;
}
.submenu {
display: none;
}
.overflow-hidden:hover .submenu {
display: inline-block;
height: 200px;
background-color: #999;
z-index: 1;
width: 100%;
position: absolute;
left: 0;
top: 20px;
}
.container {
width: 100%;
}
you do not need jquery for this, you can use the pseudo class :hover
See this fiddle: http://jsfiddle.net/yA6Lx/14/
.overflow:hover .submenu{
visibility: visible;
opacity: 1;
}
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I use HTML5, CSS, and vanilla JavaScript.
I want converted this jQuery code, and change it.
But I don't know what problem in after Code.
Originally there is a small triangular icon at the top, and it should also fill the entire screen with the menu bar. However, this code doesn't fire the event after clicking what the icon at the top would be able to create.
Before Code is this,
$(document).ready(function(){
$('.menubars').on('click', function(){
if($('.menu').hasClass('active')) {
closeMenu();
} else {
openMenu();
}
});
function openMenu() {
$('.menu').toggleClass('active');
$('.menubackground').css('left', '0');
$('.menubackground').css('top', '-810px');
$('.top').css('top', '10px');
$('.bottom').css('top', '10px');
$('.menu ul').css('visibility','visible');
$('.top').css('transform', 'rotate(45deg)');
$('.bottom').css('transform', 'rotate(-45deg)');
$('.middle').css('transform', 'rotate(45deg)');
}
function closeMenu() {
$('.menu').toggleClass('active');
$('.menu ul').css('visibility','hidden');
$('.top').css('top', '0px');
$('.bottom').css('top', '20px');
$('.top').css('transform', 'rotate(0deg)');
$('.middle').css('transform', 'rotate(0deg)');
$('.bottom').css('transform', 'rotate(0deg)');
$('.menubackground').css('left', '-2240px');
$('.menubackground').css('top', '-2240px');
}
});
and my code is like this
var menu = document.getElementsByClassName('menu');
document.onload = function () {
document
.getElementsByClassName('menubars')
.addEventListener('click', function () {
if (document.getElementsByClassName('menu').classList.contains('active')) {
closeMenu();
} else {
openMenu();
}
});
function openMenu() {
var clicked = 0;
menu.onClick() = function () {
if (clicked) {
menu.classList.add('active');
} else {
menu.classList.remove('active');
}
}
document
.getElementsByClassName('menubackground')
.style
.left = '0px';
document
.getElementsByClassName('menubackground')
.style
.top = '-810px';
document
.getElementsByClassName('top')
.style
.top = '10px';
document
.getElementsByClassName('bottom')
.style
.top = '10px';
document
.querySelectorAll('.menu ul')
.visibility = 'visible';
document
.getElementsByClassName('top')
.style
.transform = 'rotate(45deg)';
document
.getElementsByClassName('bottom')
.style
.transform = 'rotate(-45deg)';
document
.getElementsByClassName('middle')
.style
.transform = 'rotate(45deg)';
}
function closeMenu() {
menu.onClick() = function () {
if (clicked) {
menu.classList.add('active');
} else {
menu.classList.remove('active');
}
}
document
.querySelectorAll('.menu ul')
.visibility = 'hidden';
document
.getElementsByClassName('top')
.style
.top = '0px';
document
.getElementsByClassName('bottom')
.style
.top = '20px';
document
.getElementsByClassName('top')
.style
.transform = 'rotate(0deg)';
document
.getElementsByClassName('bottom')
.style
.transform = 'rotate(0deg)';
document
.getElementsByClassName('middle')
.style
.transform = 'rotate(deg)';
document
.querySelectorAll('.menubackground')
.style
.left = '-2240px';
document
.querySelectorAll('.menubackground')
.style
.top = '-2240px';
}
}
Html
<div class="menu">
<div class="menubars">
<div class="menubar top"></div>
<div class="menubar middle"></div>
<div class="menubar bottom"></div>
</div>
<div class="menubackground">
</div>
<ul class="menulinks">
<li>Home</li>
<li>About us</li>
<li>Portfolio</li>
<li>Contact us</li>
</ul>
</div>
css
*{
margin: 0;
padding: 0;
}
p{
position: absolute;
color: #222;
text-decoration: none;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
p a{
text-decoration: none;
color: purple;
}
.menu{
background-color: red;
width: 100%;
height: 100%;
}
.menubackground
{width: 2700px;
height: 2700px;
position: fixed;
left: -2240px;
z-index: 10;
top: -2240px;
transform: rotate(-45deg);
background-color: #28eca4;
transition : all 700ms ease-in;
}
.menulinks{
position: absolute;
z-index: 20;
text-align: center;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
list-style-type: none;
}
.menulinks li a{
text-decoration: none;
text-transform: uppercase;
color: #fff;
text-align: center;
font-weight: 700;
font-size: 3rem;
}
.menubars{
position: absolute;
left: 20px;
top: 20px;
cursor: pointer;
width: 50px;
height: 50px;
z-index: 20;
}
.menubars .menubar{
height: 5px;
transition : all 200ms ease-out;
position: absolute;
z-index: 30;
width: 30px;
background-color: #fff;
}
.top{
top: 0;
}
.middle{
top: 10px;
}
.bottom{
top: 20px;}
but it doesn't work. I wanna know what I fix it.
ThankYou.
First, .getElementsByClassName() returns a node list and you can't attach an event handler to a node list, you'd have to loop through the elements within the list and add the event handler to each and node lists won't have a .classList property either. Second, .getElementsByClassName() should be avoided and .querySelectorAll() should be used instead.
So, in short, you are treating the collection returned from .getElementsByClassName() as if it was an element, when it's not. You have to loop through the collection and interact with the individual elements that are in it.
I'm trying to locate which element newly rendered is under mouse pointer. (*)
Here is my code:
btn.addEventListener('click', function () {
btn.remove();
for (let i = 0; i < 10; i++) {
lst.appendChild(document.createElement('li')).textContent = 'Element ' + i;
}
requestAnimationFrame(function () { requestAnimationFrame(function () {
const chosen = document.querySelector('li:hover');
alert(chosen && 'Your mouse on ' + chosen.textContent); // do something more with chosen
}); });
});
#btn { width: 200px; height: 200px; }
#lst { width: 200px; line-height: 20px; display: block; padding: 0; }
#lst li { display: block; height: 20px; width: 200px; overflow: hidden; }
#lst li:hover { background: #ccc; }
<button id=btn>Click Me</button>
<ul id=lst><ul>
I'm confused that I need 2 requestAnimationFrame to make my code execute correctly. Removing one raf, the alert will show null instead.
The code also seems ugly to me. How to implement it more elegantly?
In case anyone care about: I'm running my code on Firefox. And the code, as a part of my Firefox extension, only need to target to Firefox 60+.
(*): The story behind may be more complex. But to keep it simple...
That's quite an interesting behavior you found here, browsers seem to not update the :hover before that second frame, even if we force a reflow or what else.
Even worse, in Chrome if you hide the <button> element using display:none, it will stay the :hover element until the mouse moves (while normally display:none elements are not accessible to :hover).
The specs don't go into much details about how :hover should be calculated, so it's a bit hard to tell it's a "bug" per se.
Anyway, for what you want, the best is to find that element through the document.elementsFromPoints method, which will work synchronously.
btn.addEventListener('click', function ( evt ) {
btn.remove();
for (let i = 0; i < 10; i++) {
lst.appendChild(document.createElement('li')).textContent = 'Element ' + i;
}
const chosen = document.elementsFromPoint( evt.clientX, evt.clientY )
.filter( (elem) => elem.matches( "li" ) )[ 0 ];
alert(chosen && 'Your mouse on ' + chosen.textContent); // do something more with chosen
});
#btn { width: 200px; height: 200px; }
#lst { width: 200px; line-height: 20px; display: block; padding: 0; }
#lst li { display: block; height: 20px; width: 200px; overflow: hidden; }
#lst li:hover { background: #ccc; }
<button id=btn>Click Me</button>
<ul id=lst><ul>
I cannot exactly answer the question why you need 2 rafs.
But i can provide you an more elegant way with async / await. Create a small function called nextTick that returns an promise. So you await for the next frame.
So you can first wait till the button is gone, create your elemens, then await again for the next painting cycle to be sure the elements are accessible
btn.addEventListener('click', async function () {
btn.remove();
await nextTick();
for (let i = 0; i < 10; i++) {
lst.appendChild(document.createElement('li')).textContent = 'Element ' + i;
}
await nextTick()
const chosen = document.querySelector('li:hover');
alert(chosen && 'Your mouse on ' + chosen.textContent); // do something more with chosen
});
function nextTick() {
return new Promise(requestAnimationFrame)
}
#btn { width: 200px; height: 200px; }
#lst { width: 200px; line-height: 20px; display: block; padding: 0; }
#lst li { display: block; height: 20px; width: 200px; overflow: hidden; }
#lst li:hover { background: #ccc; }
<button id=btn>Click Me</button>
<ul id=lst><ul>
I want to make a button inside auto generated block to change overflow from hidden to auto.
I created recursive responsive auto-grid in Less, css like this:
.container {
.container-fixed();
[class*='col-'] {
float: right;
width: 100%;
}
.make-grid(#container-xs);
.make-grid(#container-sm);
.make-grid(#container-md);
.make-grid(#container-lg);
}
.container-fixed(#gap: #grid-gap-width) {
margin-right: auto;
margin-left: auto;
padding-left: (#gap / 2);
padding-right: (#gap / 2);
}
.generate-columns(#container-width;
#number-cols;
#i: 1) when (#i =< #number-cols) {
.col-#{i} {
#single-width: #container-width / #number-cols - 0.5;
width: #i * #single-width; // 800px
}
.generate-columns(#container-width;
#number-cols;
#i + 1);
}
.make-grid(#container-width) {
#media(min-width: #container-width) {
width: #container-width;
.generate-columns(#container-width, #grid-c);
}
}
[class*='col-'] {
text-align: center;
overflow: hidden;
height: 250px;
background: #color-h;
display: block;
margin: 1px;
color: #color-text;
position: relative;
}
And now I have long text in HTML inside one of blocks no matter which one, eg. col-9 where is part hidden because I used overflow:hidden;.
What I would like to do is to create a button and on click to change from overflow:hidden; to overflow: auto;.
My question is how to do that, to change from hidden to auto, on click and again to return back to previous state on new click.
I tried something like this but that is not good:
Less - >
[class*='col-'] {
text-align: center;
overflow: hidden;
height: 250px;
background: #color-h;
display: block;
margin: 1px;
color: #color-text;
position: relative;
.show {
overflow: auto;
}
}
JS - >
var content = document.getElementsByClassName("[class*='col-']");
var button = document.getElementbyID("show");
button.onclick = function() {
if (content.className == "show") {
content.className= "";
button.inerHTML = "Read";
} else {
content.className="show";
button.inerHTML = "Close";
}
};
html - >
<div class="col-9">
<a id="button-show">Read</a>
<script src="js/read.js"></script>
<p> some long text ........ </p>
</div>
I hope I am clear enough, what I want to do.
<-- language: lang-javascript -->
$("#button-show").click(function(){
$(".col-9").toggleClass("show")
})
<-- -->
so whenever you click the button, it will add or remove the class show on your elements with the col-9 classnames
You should use .toggle() to toggle the contents between show and hide. Here is an example
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
$(document).ready(function(){
$("#button-show").click(function(){
$("#show").toggle();
});
});
[class*='col-'] {
text-align: center;
overflow: hidden;
height: 250px;
background: #color-h;
display: block;
margin: 1px;
color: #color-text;
position: relative;
}
#show {
overflow: auto;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="col-9">
<a id="button-show">Read</a>
<p id="show"> some long text ........ </p>
</div>
You have multiple problems in your code.
document.getElementsByClassName returns a list of elements (A.K.A. array), so your content.className is wrong as you accesing the array className property (which is non-existant) instead of the className property of each element inside the array. You have to iterate the array and access each element individually. Also, you are not accesing by class, but by selector (There's no class [class*='col-'], but class col-1, col-2, etc...). To select with selectors you have to use querySelector, which selects one element, or querySelectorAll which selects all elements.
Also, to hide an element you don't have to change overflow. overflow is for scrollbars. You have to change the display property to display: none and also as the class show is not a child element, it needs an & character:
[class*='col-'] {
text-align: center;
[...CSS THINGYS...]
position: relative;
&.show { // Note the & before the dot
display: none;
}
}
Your code don't has any jQuery actually. Is plain JS.
Also, the best way to attach events to HTML elements is via addEventListener, so:
var content = document.querySelectorAll("[class*='col-']");
var button = document.getElementbyID("show");
button.addEventListener("click", function() {
var anyShown = false;
content.forEach(function(element) {
if (element.className == "show") {
anyShown = true;
element.className= "";
} else {
element.className="show";
}
});
if (anyShown) {
button.inerHTML = "Read";
} else {
button.inerHTML = "Close";
}
});
If you want it in a more jQuery way you can do this, which do the same as above, but way shorter:
$("#show").on("click", function() {
if ($("[class*='col-']").hasClass("show")) {
$("#show").html("Read");
} else {
$("#show").html("Close");
}
$("[class*='col-']").toggleClass("show");
});
Relevant info:
addEventListener
getelementsbyclassname
querySelector
querySelectorAll
Less "&" operator
jQuery hasClass
jQuery toggleClass
I found solution:
JQ:
$(document).ready(function(){
$("button").click(function(){
$("p3").toggle();
});
});
CSS:
[class*='col-'] {
text-align: center;
overflow:auto;
height: 250px;
background: #color-h;
margin: 1px;
color: #color-text;
position: relative;
.border-radius(10px);
p3 {
margin: 10px ;
padding: 5px;
width: 95%;
text-align: justify;
display: none;
}
}
HTML:
<div class="col-9">
<button>Read</button>
<h1>TITLE</h1>
<p>some tekst.</p>
<p3>Tekst i want to hide ....</p3>
</div>
I am trying to exit a for loop on a click function in javascript. But somewhere I am not getting the correct way to do so. Please take a look to my code and help me to get out of this problem.
What I am trying to do is want to show different div elements inside the li element with a click on li and want to hide the same with a click on div.
here is the link for jsfiddle
Here is the code below :: (html)
<ul>
<li>
<div style="position: fixed; width: 400px; height: 400px; background-color: #0c6; display: none;"></div>
</li>
<li></li>
<li></li>
</ul>
Style
ul {
width: 100%;
height: 100%;
}
ul li {
list-style: none;
float: left;
width: 100px;
height: 100px;
background-color: green;
border: 1px solid;
}
javascript
var a = document.getElementsByTagName('li');
for (i = 0; i < a.length; i++) {
a[i].onclick = function () {
var b = this.getElementsByTagName('div')[0];
setTimeout(function () {
b.style.display = 'block';
}, 1000);
b.onclick = function () {
this.style.display = 'none';
};
};
}
i did an example on jsFiddle ( http://jsfiddle.net/aRWhm/ ) , the idea is to know when i'm over lets say the intersection between the red and the blue circle.
but the problem is that every time i reach the intersection, the class "is-over" of the red circle is removed.
Html:
<div>
<span id="Div1"></span>
<span id="Div2"></span>
<span id="Div3"></span>
<span id="Div4"></span>
</div>
CSS:
div {
display: block;
margin: 0 auto;
overflow: hidden;
width: 950px;
}
span {
display: block;
position: absolute;
opacity: 0.5;
border-radius: 999px;
z-index: 1;
}
#Div1 {
background-color: #FF0000;
height: 200px;
left: 50px;
top: 80px;
width: 200px;
}
#Div2 {
background-color: #0000FF;
height: 150px;
left: 40px;
top: 230px;
width: 150px;
}
#Div3 {
background-color: #008000;
height: 250px;
left: 100px;
top: 190px;
width: 250px;
}
#Div4 {
background-color: #FFFF00;
height: 100px;
left: 200px;
top: 130px;
width: 100px;
}
JavaScript:
$(document).ready(function () {
$("#Div1").hover(
function () {
$(this).addClass("is-over");
},
function () {
$(this).removeClass("is-over");
}
);
$("#Div2").hover(
function () {
$(this).addClass("is-over");
},
function () {
$(this).removeClass("is-over");
}
);
$("#Div3").hover(
function () {
$(this).addClass("is-over");
},
function () {
$(this).removeClass("is-over");
}
);
$("#Div4").hover(
function () {
$(this).addClass("is-over");
},
function () {
$(this).removeClass("is-over");
}
);
});
Here you go.
First, the Code:
(function($){
$.mlp = {x:0,y:0}; // Mouse Last Position
function documentHandler(){
var $current = this === document ? $(this) : $(this).contents();
$current.mousemove(function(e){jQuery.mlp = {x:e.pageX,y:e.pageY}});
$current.find("iframe").load(documentHandler);
}
$(documentHandler);
$.fn.ismouseover = function(overThis) {
var result = false;
this.eq(0).each(function() {
var $current = $(this).is("iframe") ? $(this).contents().find("body") : $(this);
var offset = $current.offset();
result = offset.left<=$.mlp.x && offset.left + $current.outerWidth() > $.mlp.x &&
offset.top<=$.mlp.y && offset.top + $current.outerHeight() > $.mlp.y;
});
return result;
};
})(jQuery);
$(document).ready(function () {
$("#myDiv").mousemove(
function() {
$("#myDiv").children("span").each(function(){
if($(this).ismouseover())
$(this).addClass("is-over");
else
$(this).removeClass("is-over");
});
});
});
Now an explanation:
I stole the .ismouseover() code shamelessly from this answer by Ivan Castellanos and repurposed it to your needs. Form there I used a .mousemove() event to fire every time you're in the parent container, which you can see in this fiddle needed to be given height and width parameters to ensure that it had a bounding box.
Lastly I simply check to see which circles you're over, and add the is-over class to them. The Fiddle is based off Anton's work, although it provides intersection support instead of moving one to the top.
Hope this helps.