I have a div that I show if the mouse is hovering over a different div, then when the mouse leaves the div is hidden.
In my mousemove callback function I say
$('#divToShow').show(), however I'm wondering if this is inefficient as mousemove events are fired very frequently, and if that div is already shown then it's calling show() for no reason.
Would it be more efficient to check if the div is hidden, and only show it then? Like this:
if ($('#divToShow').is(":hidden")){
$('#divToShow').show();
}
Or another solution would be to have a boolean variable that is set to true the first time the div is shown, then set to false on mouseleave.
Does anyone have any information on an efficient way to show a div in a mousemove function?
Since you're using Jquery, a more efficient way to do what you want is using a .hover(handlerIn, handlerOut) callback, so you don't need to worry about creating a flag or something like that.
The handlerIn will be triggered only one time, when the mouse enter on the target div (as you can see on the console.log('show') call). The handlerOut will also be executed only one time, when the mouse leaves the target div.
On the example below, when the mouse hover #div-b the #div-a content will be visible and when it leaves, the content will be hidden:
$(function() {
$('#div-b').hover(
function() {
console.log('show');
$('#div-a').show();
},
function() {
console.log('hide');
$('#div-a').hide();
}
);
});
#div-a {
display: none;
padding: 20px;
}
.wrapper {
margin: auto;
background-color: darkcyan;
color: white;
height: 100px;
margin-bottom: 10px;
}
#div-b {
padding: 20px;
height: 50px;
background-color: black;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div id="div-a">
I'm visible!
</div>
</div>
<div id="div-b">
Hover me
</div>
First of All , i let you inspect source code of $.fn.show, then , see , at the end , my answer :
show: function() {
return showHide( this, true );
}
And showHide source code is :
function showHide( elements, show ) {
var display, elem, hidden,
values = [],
index = 0,
length = elements.length;
for ( ; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
values[ index ] = dataPriv.get( elem, "olddisplay" );
display = elem.style.display;
if ( show ) {
// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !values[ index ] && display === "none" ) {
elem.style.display = "";
}
// Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
if ( elem.style.display === "" && isHidden( elem ) ) {
values[ index ] = dataPriv.access(
elem,
"olddisplay",
defaultDisplay( elem.nodeName )
);
}
} else {
hidden = isHidden( elem );
if ( display !== "none" || !hidden ) {
dataPriv.set(
elem,
"olddisplay",
hidden ? display : jQuery.css( elem, "display" )
);
}
}
}
// Set the display of most of the elements in a second loop
// to avoid the constant reflow
for ( index = 0; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
elem.style.display = show ? values[ index ] || "" : "none";
}
}
return elements;
}
So :
You don't need to check if it is hidden or not $().is(':hidden') because it is already checked in show function (See : if ( elem.style.display === "" && isHidden( elem ) ) { ....)
Use mouseEnter to set a flag (or just show) and on mouseLeave set the flag to the opposite value (or just hide). mouseMove may not be best event since it may be fired more than desired. You may not even keep track of a flag if you just want to show and hide an element.
Can't you use .hover() and .toggle() functions? is mousemove must?
https://jsfiddle.net/tvwpdxum/1/
A better solution would be to use a mouseEnter and mouseLeave like this snippet below:
This is in javaScript but will give you a better idea. If you want it in JQuery and having problems with it I can write it on Pluncker for you. Hope this will help
function show_hide(id) {
var e = document.getElementById(id);
if (e == null){
} else {
if (!e.classList.contains('showClass'))
e.className += " showClass";
else
e.className = "myclass";
}
}
.myclass {
opacity: 0;
margin-top: 25px;
font-size: 21px;
text-align: center;
-webkit-transition: opacity 1s ease-in;
-moz-transition: opacity 1s ease-in;
-o-transition: opacity 1s ease-in;
-ms-transition: opacity 1s ease-in;
transition: opacity 1s ease-in;
}
.showClass{ opacity: 1 }
<div onmouseover="show_hide('deletebutton')" onmouseout="show_hide('deletebutton')">
// image
<div class="myclass" id="deletebutton">DELETE</div>
</div>
Updated
function show() {
$("#deletebutton").show();
}
function hide() {
$("#deletebutton").hide();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div onmouseover="show()" onmouseout="hide()">
// image
<div class="myclass" id="deletebutton" style="display:none;">DELETE</div>
</div>
Related
When I want to yield execution of my javascript and allow the browser to apply styles etc. before I continue, I tend to use the common technique of setTimeout with a delay of 0 to have a callback queued at the end of the event loop. However, I came across a situation where this doesn't seem to be working reliably.
In the snippet below, I have an active class that applies a transition to the chaser element.
When I hover over a target div, I want to remove the active class from the chaser element, move the chaser to a new location, then reapply the active class. The effect should be that the o should immediately vanish, and then fade in its new location. Instead, both the opacity and top have the transition applied, so the o slides from position to position, most of the time.
If I increase the inner timeout's delay to 10, it starts to behave as I originally intended. If I set it to 5, then it sometimes does and sometimes doesn't.
I would have expected any setTimeout to have queued my callback until after the style updates have been applied, but there's a clear race condition here. Am I missing something? Is there a way to guarantee the order of updates?
I'm on Chrome 56 on macOS and Windows, haven't tested other browsers yet.
(I know I can achieve this in other ways such as applying the transition to only the opacity property - please consider this a contrived example to demonstrate the particular question about ordering style updates).
var targets = document.querySelectorAll('.target');
var chaser = document.querySelector('#chaser');
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('mouseenter', function(event) {
chaser.className = '';
setTimeout(function() {
// at this point, I'm expecting no transition
// to be active on the element
chaser.style.top = event.target.offsetTop + "px";
setTimeout(function() {
// at this point, I'm expecting the element to
// have finished moving to its new position
chaser.className = 'active';
}, 0);
}, 0);
});
}
#chaser {
position: absolute;
opacity: 0;
}
#chaser.active {
transition: all 1s;
opacity: 1;
}
.target {
height: 30px;
width: 30px;
margin: 10px;
background: #ddd;
}
<div id="chaser">o</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
What you need to listen for is transitionend event before doing anything else. You can read up on MDN about transitionend event. Btw, setTimeout should never be used to guarantee timing.
EDIT: This is for reference after clarification from OP. Whenever a style change occurs to an element, there is either a reflow and/or repaint. You can read more about them here. If the second setTimeout is ran before the first reflow, then you get the sliding effect. The reason why 10ms will lead to the desired effect is because .active class is added after the offsetTop property has been adjusted (leading to transition property applied after the element has changed it's offsetTop). Usually, there are 60fps (i.e: ~16ms per frame) which means that you have a 16 ms window to do anything before the new styles are applied. This is why a small delay of 5ms will sometimes lead different results.
TL:DR - The browser asks JS and CSS every 16ms for any updates, and calculates what to draw. If you miss the 16ms window, you can have completely different results.
You are calling a setTimeout() timing method inside another setTimeout() timing method.
My thought is, why not call the both setTimeout() method separately like so:
The first setTimeout() method should execute first, then at end of execution, it should call the second setTimeout() method.
Here is a working script for moving the chaser:
function _( id ) { return document.getElementById( id ); }
window.addEventListener( 'load', function() {
var targets = document.querySelectorAll('.target');
var chaser = document.querySelector('#chaser');
setTopPosition( targets );
function setTopPosition( targets ) {
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('mouseenter', function(event) {
chaser.className = '';
_( 'status' ).innerText = chaser.className; // to inspect the active class
setTimeout(function() {
/* at this point, I'm expecting no transition // to be active on the element */
chaser.style.top = event.target.offsetTop + "px";
}, 0);
// check if charser.className == ''
if ( chaser.className == '') {
setClassName();
} else {
alert( 0 );
}
}); // addEventListener
} // for
} //function setTopPosition( targets )
function setClassName() {
setTimeout(function() {
/* at this point, I'm expecting the element to have finished moving to its new position */
chaser.className = 'active';
_( 'status' ).innerText = chaser.className;
}, 0);
} // function setClassName()
});
HTML:
<div id="chaser">o</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
<div id="status">status</div>
CSS:
#chaser { position: absolute; opacity: 0; }
#chaser.active { transition: all 1s; opacity: 1; }
.target { height: 30px; width: 30px; margin: 10px; background: #ddd; }
I am trying to pass data attribute in custom element as an object but while receiving inside attachedCallback method getting value "[object object]" in a string form.
So can anyone help me to figure out what is the work around to get the attributes as an object inside custom-element(web component).
code sample
<script>
class myElements extends HTMLElement {
createdCallback() {
this.innerHTML = `<h1>Hello</h1>`;
}
attachedCallback() {
console.log(this.getAttribute('data'));
}
}
document.registerElement('my-element', myElements);
</script>
custom element tag
<script>
var object = { key1: 111, key2: 222, key3: function(){return "hi"}, key4:[1,2,3]};
function changeHandler() {
page('/default', function() {
// some logic to decide which route to redirect to
if (admin) {
page.redirect('/admin');
} else {
page.redirect('/guest');
}
});
}
</script>
<my-element data="object" onchange="changeHandler"></my-element>
Note: suppose that <my-element> is a dropdown which gives user option to choose some value.
Solution: Still no native solution in custom-element specs(v0 and v1).
Since Custom Elements doesn't support data binding so we need a sugaring layer for that (e.g., Polymer or SkateJS) as mention by #tony in the comment.
Try by converting object to JSON string,
var object = { key1: 111, key2: 222};
JSON.stringify(object);
Then when you want to get the value, parse it back to object
JSON.parse(this.getAttribute('data'));
Custom Elements does not modify the standard HTML element attribute behaviour which is always of type string.
Because of that, you shoud instead either:
Send a change Event from your custom element that will trigger the onchange handler.
Register you object/function callback via a custom element method.
Modify a state attribute that will be observed (with Mutation Observer) by its container.
If you want to use attribute anyways you can always use eval().
Example with solution 1 with call to changeHandler():
//define custom element
class DropDown extends HTMLElement
{
connectedCallback ()
{
var span = this.querySelector( 'span' )
//define ul list
var ul = this.querySelector( 'ul' )
ul.onclick = ev => {
if ( this.value != ev.target.textContent )
{
this.value = ev.target.textContent
this.setAttribute( 'value', this.value )
span.textContent = this.value
this.dispatchEvent( new CustomEvent( 'change' ) )
}
}
//show or hide
this.onclick = ev => ul.classList.toggle( 'show' )
}
}
customElements.define( 'drop-down', DropDown )
drop-down {
position: relative ;
cursor: pointer ;
display: inline-block ;
}
drop-down > span {
border: 1px solid #aae ;
padding: 2px 5px ;
}
drop-down > ul {
position: absolute ;
top: 4px ; left: 5px ;
list-style: none ;
outline: 1px solid #aae ;
padding: 0 ;
display: inline-block ;
opacity: 0 ;
transition: all 0.3s ease-out ;
background: white ;
visibility: hidden ;
z-index: 10 ;
}
drop-down > ul.show {
opacity: 1 ;
visibility: visible ;
}
drop-down > ul > li {
padding: 2px 5px ;
}
drop-down > ul > li:hover {
background: lightgoldenrodyellow ;
}
<drop-down onchange="changeHandler()">
<span>select value</span>
<ul>
<li>Chrome</li>
<li>Firefox</li>
<li>Opera</li>
<li>Safari</li>
</ul>
</drop-down>
<script>
function changeHandler ()
{
console.log( 'changeHandler()', event.target.value )
}
</script>
I have all 26 letters in the alphabet and I want to make it so that when you click on it will fade to a lower opacity but if you click on it again it will come back to 1.0 opacity. I have not a single clue on how I would go about specifically choosing the letter that was clicked on and I also cant seem to figure out how I would toggle it to a specific opacity.
$(document).ready(function() {
$(".alphabetLetter").click(function(event) {
var x = event.target||event.srcElement;
if(event.target.style.opacity == 1.0){
$(x).fadeTo('slow',0.5);
} else if(event.target.style.opacity == 0.5){
$(x).fadeTo('slow',1.0);
}
});
});
You can select for the currently clicked element by using $(this) inside the click event handler.
$(document).ready(function() {
$(".alphabetLetter").click(function(event) {
if ($(this).css('opacity') == '1')
$(this).animate({'opacity':0})
else
$(this).animate({'opacity':1})
});
});
Here's a full simple example:
HTML:
<p class="alphabet">abcdefghijklmnopqrstuvwxyz</p>
JavaScript:
// Your container
var $wrap = $('p.alphabet');
// Wrap all letters in <span>
$wrap.html(function(){
return $(this).text().replace(/./g, '<span class="letter">$&</span>');
});
// Attach the event
$wrap.find('.letter').click(function(){
var isHidden = $(this).css('opacity') == .5;
$(this).fadeTo(300, isHidden ? 1 : .5);
});
Demo: http://jsbin.com/eyajel/1/edit
The opacity can just be done with css transitions and regular old styles.
The CSS
.letter {
transition: opacity 1s;
color: red;
cursor: pointer;
}
.letter.active {
opacity: 0.2;
}
The Javascript
$('body').on('click', '.letter', function(e){
$(this).toggleClass('active');
});
Here is a JSFiddle as an example. It also includes a way to take a string of letters and turn them into markup that works with this: http://jsfiddle.net/PFjnB/1/
I am using jquery .toggle() to show a div on a page that has display:none on page load. However, under the default settings jquery inserts display:block, where I would want display:table-cell. How can I achieve this? My attempt so far:
<div class="mydiv" style"display:none">test</div>
.mydiv {
display:table-cell;
}
$("a#showdiv").click(function() {
$(".mydiv").toggle();
Use .toggleClass() instead and use css for the styling..
html
<div class="mydiv table-hidden">test</div>
css
.mydiv {
display:table-cell;
}
.mydiv.table-hidden{
display:none;
}
jquery
$("a#showdiv").click(function() {
$(".mydiv").toggleClass('table-hidden');
}
Use CSS for the styling #Gaby aka G. Petrioli or use the .css() method in jQuery.
HTML
<div class="mydiv">test</div>
jQuery
$("a#showdiv").click(function() {
if($(".mydiv").css("display") == "none"){
$(".mydiv").css("display", "table-cell");
} else {
$(".mydiv").css("display", "none");
}
});
What you have should work already.
In jQuery, unless the style attribute of the element has display initially set to something else other than none, when the show is called, all it does is remove the style attribute for display. It doesn't set it block.
You can see for yourself in this fiddle that when the button is clicked, the display that is set in the CSS is what the element is set to.
Here's the relevant code in the jQuery source:
function showHide( elements, show ) {
var display, elem, hidden,
values = [],
index = 0,
length = elements.length;
for ( ; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
values[ index ] = data_priv.get( elem, "olddisplay" );
display = elem.style.display;
if ( show ) {
// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !values[ index ] && display === "none" ) {
elem.style.display = "";
}
// Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
if ( elem.style.display === "" && isHidden( elem ) ) {
values[ index ] = data_priv.access( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
}
} else {
if ( !values[ index ] ) {
hidden = isHidden( elem );
if ( display && display !== "none" || !hidden ) {
data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css(elem, "display") );
}
}
}
}
// Set the display of most of the elements in a second loop
// to avoid the constant reflow
for ( index = 0; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
elem.style.display = show ? values[ index ] || "" : "none";
}
}
return elements;
}
I should note that toggling classes will usually be faster in general because there is less information to check.
.display-none {
display: none;
}
div.display-table-cell {
display: table-cell;
}
<button id="showdiv">Show/Hide</button>
<div class="mydiv display-none">test</div>
<div class="mydiv display-none">test</div>
<div class="mydiv display-none">test</div>
<div class="mydiv display-none">test</div>
$("#showdiv").click(function() {
$(".mydiv").toggleClass('display-table-cell');
});
http://jsfiddle.net/7q27y/
If you understand CSS precedence as well, you don't technically "need" the div. on the latter:
div.display-table-cell {
display: table-cell;
}
http://jsfiddle.net/7q27y/
Of course, this is due to it's precedence and the fact it falls after the other statement, which both would calculate as the same precedence otherwise. See:
http://jsfiddle.net/7q27y/2/
I made this script, which opens a div with the right class and close the others.
function showhide(id) {
if (document.getElementById) {
var divid = document.getElementById(id);
var divs = document.getElementsByClassName("hideable");
for (var i = 0; i < divs.length; i = i + 1) {
divs[i].style.display = "none";
}
divid.style.display = "block";
}
return false;
}
Is it possible to make some animation, like fadout, easeout instead of just showing it by display options?
You could try this
function showhide(id) {
if (document.getElementById) {
var divid = document.getElementById(id);
var divs = document.getElementsByClassName("hideable");
for (var i = 0; i < divs.length; i = i + 1) {
$(divs[i]).fadeOut("slow");
}
$(divid).fadeIn("slow");
}
return false;
}
Have a look at this fiddle "http://jsfiddle.net/9jtd3/"
There are many more techniques provided by Jquery library, You should have a look at that too.
You can use slideDown() and slidUp() of jQuery
$( document.body ).click(function () {
if ( $( "div:first" ).is( ":hidden" ) ) {
$( "div" ).slideDown( "slow" );
} else {
$( "div" ).slideUp("slow");
}
});
This example will toggle multiple elements with the same class name. This example does not need jquery.
HTML:
<button onclick="fadeInAndOut(this)" style="width:100%">Toggle 1</button>
<div class="accordianPanel acordianPanelHidden accordianPanelStyle">Panel 1</div>
<button onclick="fadeInAndOut(this)" style="width:100%">Toggle 2</button>
<div class="accordianPanel acordianPanelHidden accordianPanelStyle">Panel 2</div>
Javascript:
function fadeInAndOut(thz) {
var elmt = thz.nextElementSibling;//Get the element that is below the button that
//was just clicked
elmt.classList.toggle("acordianPanelHidden");//Toggle the class which changes
//attributes which triggers the `transition` CSS
}
CSS
.accordianPanel {
opacity: 1;
height:100%;
transition: all 1s;
}
.accordianPanel.acordianPanelHidden {
opacity: 0;
height: 0px;
visibility:hidden;/* This must be used or some strange things happen -
What happens is that even though the content of the panel is not shown
any buttons in the content can still be clicked -
So basically there are invisible buttons that can accidently get clicked -
if the visibility is not set to hidden - And the visibility doesn't need to be explicitly changed to visible
from hidden in order to show the content
because if visibility:hidden is not used then by default the content is
displayed -
*/
}
.acordianPanelShown {
height: 100%;
width: 100%;
opacity: 1;
}
.accordianPanelStyle {
background:red;
}
This will surely solve your problem.
You can use .fadeOut() directly if you have included jQuery library in your script.
This is way easier with only CSS.
You make a class
div {
display:block;
transition: all .2s ease-out;
}
.hidden {
display:none;
}
And with javascript, you apply or remove the class hidden when you want to. jQuery animation lib is wayyyy far from good to be used. It's clunky, and ressource eating for your user. CSS works with your GPU instead, allowing a more fluid animation.
If You are using Jquery then another way to do this is
function showhide(id) {
$(".hideable").fadeOut("slow");
$("#" + id).fadeIn("slow");
}
Assuming "hideable" as className in your group of divs
Good luck.
You can do that using a Library like jQuery or something.
You can sure make it using plain javascript, but there's no point doing that since jQuery is an amazing library.
See some examples of show and hide