I am having a bit of trouble combining the HOVER and FOCUS events with jquery. This is what I had originally:
$("input,textarea").focus(function () {
$(this).parent().siblings('div.exp').removeClass('hide');
$(this).parent().siblings('div.exp').addClass('show');
});
$("input,textarea").blur(function (){
$(this).parent().siblings('div.exp').removeClass('show');
$(this).parent().siblings('div.exp').addClass('hide');
if ($(this).val().length <= 0) {
$(this).siblings('span.warning').removeClass('hide');
$(this).siblings('span.warning').addClass('show');}
else {
$(this).siblings('span.warning').removeClass('show');
$(this).siblings('span.warning').addClass('hide');
}
});
Basically, I have a user contact form with rows like the one below:
<div class="row">
<p><label>Your Name</label><input type="text" name="name" id="name" value=""/><span class="warning">Your name is missing</span></p>
<div class="exp">Who am I to address?</div>
</div>
The point of my Jquery code is to bring forth a hidden div (exp) when the user focuses any one input or textarea element as well as checking if the value of said input is not empty when unfocusing (blur) the element. (I haven't really gotten down to validation yet so checkin for the string length right now is just a temporary filler). Should the element have a string smaller or equal than 0, then span.warning is to be 'shown' to the user.
This is all working nicely.
Where I get stuck is the following:
I want to add in hover but without conflicting with focus. My desired final effect is this:
You hover any input or textarea and you get the div.exp to show up (exp is for explanation). You focus any input or area and the div.exp stays there, even if you go about hovering any other inputs or textareas. Should you hover an input that is already focused, nothing should happen.
So, in a nutshell, the focus and hover elements should work 'independently' so to speak. Not sure if I made myself clear but oh well, I tried =)
Cheers
G
Your code can be significantly shortened by using .hide() and .show() and chaining the events. I posted a demo here.
$(document).ready(function(e){
// hide all explanations and warnings
$('.exp, .warning').hide();
// add focus, blur and hover events to all inputs & textareas
$('input,textarea')
// if focused, show the explanation
.focus(function(){
// show explanation on focus (and add a class so the hover function knows not to hide it
$(this).addClass('focused').closest('.row')
.find('.exp').show()
.find('.warning').hide();
})
.blur(function(){
// hide explanation on blur
$(this).removeClass('focused').closest('.row').find('.exp').hide();
if ($(this).val().length < 1) {
// input is empty, show the warning
$(this).closest('.row').find('.warning').show();
} else {
// input is not empty, hide the warning... you might want to add the validation here
$(this).closest('.row').find('.warning').hide();
}
})
.hover(function(){
// show explanation when hovered
$(this).closest('.row').find('.exp').show();
},function(){
// hide explanation if the input is not focused
if ($(this).is(':not(.focused)')) $(this).closest('.row').find('.exp').hide();
})
});
You can set a flag to the input or textarea while it is focused to avoid conflict with your hover event. If the flag is set to true when the over or out event is fired, its code is not executed. The following code show the idea (I have not test it).
$("input,textarea").focus( function()
{
$(this).parent().siblings( 'div.exp' ).removeClass( 'hide' ).addClass( 'show' );
$(this).data( "hasFocus", true );
} );
$("input,textarea").blur(function()
{
$(this).parent().siblings( 'div.exp' ).removeClass( 'show' ).addClass( 'hide' );
if( $(this).val().length <= 0 )
$(this).siblings( 'span.warning' ).removeClass( 'hide' ).addClass( 'show' );
else
$(this).siblings( 'span.warning' ).removeClass( 'show' ).addClass( 'hide' );
$(this).data( "hasFocus", false );
});
$("input,textarea").hover( function()
{
// Over event
if( typeof $(this).data( "hasFocus" ) != undefined && !$(this).data( "hasFocus" ) )
$(this).parent().siblings( 'div.exp' ).removeClass( 'hide' ).addClass( 'show' );
},
function()
{
// Out event
if( typeof $(this).data( "hasFocus" ) != undefined && !$(this).data( "hasFocus" ) )
$(this).parent().siblings( 'div.exp' ).removeClass( 'show' ).addClass( 'hide' );
} );
Related
This is more of a tip than a question, but i hope others find this useful.
Basically i needed to make the lightbox slider called 'Swipebox' auto transition to the next slide, I looked online for help but found nothing.
to add this feature to the plugin i added this code:
setInterval(function(){
$this.getNext(); // Auto transitions each slide
}, 5000);
The file for this JS is called 'jquery.swipebox.js'. This is the code where you want to place the code previously mentioned:
/**
* Navigation events : go to next slide, go to prevous slide and close
*/
actions : function () {
var $this = this,
action = 'touchend click'; // Just detect for both event types to allow for multi-input
if ( elements.length < 2 ) {
$( '#swipebox-bottom-bar' ).hide();
if ( undefined === elements[ 1 ] ) {
$( '#swipebox-top-bar' ).hide();
}
} else {
$( '#swipebox-prev' ).bind( action, function( event ) {
event.preventDefault();
event.stopPropagation();
$this.getPrev();
$this.setTimeout();
} );
$( '#swipebox-next' ).bind( action, function( event ) {
event.preventDefault();
event.stopPropagation();
$this.getNext();
$this.setTimeout();
} );
}
$( '#swipebox-close' ).bind( action, function() {
$this.closeSlide();
} );
// THIS IS THE NEW CODE ADDED:
setInterval(function(){
$this.getNext(); // Auto transitions each slide
}, 5000);
},
This function is around about 550 lines down.
Also, to make the slideshow loop back around, you will need to change the option called 'loopAtEnd' from FALSE to TRUE. This is located at the top of the document.
loopAtEnd: true,
Hope this helped :)
On top of page it has a menu that opens with click on it:
menu opens with click
I need to change it to open with hover, the code is this:
$(document).on('click','.btn-open-mobile',function(){
var width = $(window).width();
if(width > 1024){
if($('body').hasClass('home') && !$('.box-vertical-megamenus').is('.hiden_content')){
if($('#nav-top-menu').hasClass('nav-ontop') || $('#header').hasClass('option6') || $('#header').hasClass('option5') ){
}else{
return false;
}
}
}
$(this).closest('.box-vertical-megamenus').find('.vertical-menu-content').slideToggle();
$(this).closest('.title').toggleClass('active');
if( width < 768 ){
$('.main-menu .navigation-main-menu').hide();
}
return false;
})
when I replace "click" with "hover", it works But problem is menu is open only if cursor is on "three lines icon".
How to force this menu continue to stay open until cursor moved out of sub menus.
sorry for dirty code, I'm newbie.
Make sure you have a proper parent/child structure.
You need to target the entire div and not only the button when using hover.
jQuery Mousenter
https://api.jquery.com/mouseenter/
Should give you more freedom to play with your menu.
$( "nav" )
.mouseenter(function() {
$( "p:first", this ).text( "mouse enter" );
$( "p:last", this ).text( ++n );
})
.mouseleave(function() {
$( "p:first", this ).text( "mouse leave" );
});
I have a html page with over 1500 element !!!
and a menu .
when I click somewhere on document , the page locked for over 2 seconds (at least).
After some search I found this block of code in JQuery.UI :
// Clicks outside of a menu collapse any open menus
this._on( this.document, {
click: function( event ) {
if ( !$( event.target ).closest( ".ui-menu" ).length ) {
this.collapseAll( event );
}
// Reset the mouseHandled flag
this.mouseHandled = false;
}
});
the if statement fires over and over and it become problem .
How this could fix ?!
Links that are subject to display: none aren't in the default tabbing order. However, when they're revealed - e.g. CSS for a drop-down menu reveals a sub-menu when a parent link gains focus - they still aren't in the tabbing order. Presumably JavaScript is required, but simply setting tabindex="0" does nothing.
The problem here is that as soon as you tab off the "Top level page with child" link, the CSS is updated and the parent of the anchor becomes display:none before the anchor can receive focus. You will need to use JavaScript to solve this problem and delay the change in the CSS using a timeout until you can determine whether the loss of focus has resulted in the focus shifting to the child element.
Setting tabindex="0" when the parent is display:none will not help, display:none means that the content might as well not be in the document.
I've come up with a solution that basically works: http://codepen.io/gyrus/pen/waKjKv/ There seem to be some issues in IE, I'm working on that. But the general idea is:
Add a small delay before hiding the drop-down
Set a data attribute to flag whether any links inside the drop-down have focus, and check this before hiding
This is just the jQuery, check the pen for more:
jQuery( document ).ready( function( $ ) {
$( '.nav' ).on( 'mouseenter focusin', '.menu-level-0.menu-item-has-children', function( e ) {
var el = $( this );
// Show sub-menu
el.attr( 'aria-expanded', 'true' )
.find( '.sub-menu-wrapper' ).show();
}).on( 'mouseleave focusout', '.menu-level-0.menu-item-has-children', function( e ) {
var el = $( this );
// Only hide sub-menu after a short delay, so links get a chance to catch focus from tabbing
setTimeout( function() {
var smw = el.find( '.sub-menu-wrapper' );
if ( smw.attr( 'data-has-focus' ) !== 'true' ) {
el.attr( 'aria-expanded', 'false' );
smw.hide();
}
}, 100 );
}).on( 'focusin', '.sub-menu-wrapper', function( e ) {
var el = $( this );
el.attr( 'data-has-focus', 'true' );
}).on( 'focusout', '.sub-menu-wrapper', function( e ) {
var el = $( this );
el.attr( 'data-has-focus', 'false' );
// Hide sub-menu on the way out
el.hide().parents( '.menu-level-0' ).attr( 'aria-expanded', 'false' );
});
});
I'm using the 'nested-list' plugin for jQuery Mobile, this one:
The problem is that when you use more than one level the plugins fails going back. For example, in the fiddle I have created I can go to 'Test 1.2.1' without problem, If I going back 1 level it works fine and I go to 'Test 1.2', but then if I tried to go up one level more (it was 'Test1') it goes up 2 levels (to 'Test').
I have checked the plugin code but I can't find the problem and I have left a message in the Git forum with no answer. Maybe someone could help me here.
Thanks in advance!
Fiddle
Looking at the plugin code, it is only designed for one level deep nesting. This is because the developer chose to remove created subpages each time you click on a parent LI. So when you get to the second level of depth, its parent has been removed from the DOM and you have to click the back button twice to get to the original page.
I have made some changes to the plugin code that should solve this problem:
In _attachBindings, I have commented out the line that removes previously created subpages:
_attachBindings: function() {
this._on({
"click": "_handleSubpageClick"
});
this._on( "body", {
"pagechange": function(){
if ( this.opening === true ) {
this.open = true;
this.opening = false;
} else if ( this.open === true ) {
//Don't remove the old LI
//this.newPage.remove();
this.open = false;
}
}
});
},...
Then in _handleSubpageClick, I check if the subpage already exists in the DOM (via data attribute added when creating the page). If not, we go through the existing code that creates the subpage, and then in the end I store the created subpage id in a data attribute on the parent LI. If it does exist we just navigate to that page.
_handleSubpageClick: function( event ) {
if( $(event.target).closest( "li" ).children( "ul" ).length == 0 ) {
return;
}
this.opening = true;
//see if we already created the subpage
var $li = $(event.target).closest( "li" );
var pid = $li.data("nextpageid");
if (pid && pid.length > 0){
this.pageID = pid;
} else {
this.newPage = $( this.options.page ).uniqueId();
this.nestedList = $( event.target ).children( "ul" )
.clone().attr( "data-" + $.mobile.ns + "role", "listview" )
.css( "display", "block" );
this.pageName = (
$( event.target.childNodes[0] ).text().replace(/^\s+|\s+$/g, '').length > 0 )?
$( event.target.childNodes[0] ).text() : $( event.target.childNodes[1] ).text();
this.pageID = this.newPage.attr( "id" );
// Build new page
this.newPage.append(
$( this.options.header ).find( "h1" ).text( this.pageName ).end()
).append(
$( this.options.content )
).find( "div.ui-content" ).append( this.nestedList );
$( "body" ).append( this.newPage );
//save subpage id as data attribute of the LI
$li.data("nextpageid", this.pageID);
}
$( "body" ).pagecontainer( "change", "#" + this.pageID );
}...
Here is your updated FIDDLE
I removed the external link to the plugin and instead copied all the code into the javascript pane and made the edits. You should be able to copy that code directly and use as the updated plugin. (Of course I did this quickly and have not rigorously tested it, so make sure it works for you).