I'm writing a jquery plugin to display a jquery ui dialog when links are clicked to provide a confirmation dialog before the link is followed.
The problem i'm having is that when closing the dialog using the "Yes" button, the plugin uses $(element).trigger( 'click' ); to fire the click event on the original anchor element.
This does not cause the browser to follow the link, however a second click with my mouse after the dialog closes does work.
The plugin is used like this $('a').submitConfirm();
Here is the plugin
;(function ( $, window, document, undefined )
{
var pluginName = "submitConfirm";
var Plugin = function( element )
{
var confirmed = false;
var dialog = $( '<div style="display:none;">' )
.html( 'Visit this link?' )
.dialog(
{
modal: true,
title: 'Visit Link?',
autoOpen: false,
buttons :
[
{
text: 'Yes',
click: function( event )
{
confirmed = true;
dialog.dialog( "close" );
$(element).trigger( 'click' );
}
},
{
text: 'No',
click: function( event )
{
confirmed = false;
dialog.dialog( "close" );
}
}
]
});
$(element).bind( 'click',
function( event )
{
if ( ! confirmed )
{
dialog.dialog( "open" );
event.preventDefault();
}
});
};
// Init the plugin
$.fn[pluginName] = function( options )
{
return this.each(function ()
{
// Prevent re-instantiation
if ( !$.data(this, 'plugin_' + pluginName) )
{
$.data(this, 'plugin_' + pluginName,
new Plugin( this, options ));
}
});
};
})( jQuery );
You have to pass a function containing what you want to do to the plugin.
Add this line when you are setting the default parameters for the plugin at the bottom of your javascript.
$(function()
{
$('a').submitConfirm(
{
html: 'Are you sure?',
onConfirm: function(event){ // Do what you want in this function.
alert('Confirmed.. Now what?.. Redirect?.. ?? ');
// window.location = $(this).attr('href'); // redirect
},
beforeShow: function( dialog )
{
dialog.html( 'visit google?' );
}
});
});
Update
Check out this JSfiddle --> http://jsfiddle.net/kmTtQ/6/
I changed the lines below. Basically, we want to add a .click event to the element, then .trigger('click') that click.
if ( confirmed ){
console.log( element, elementEvent, event.isDefaultPrevented );
// .on() = jQuery 1.7+, for < 1.7 use .bind or .live. Aliases of .on() as of 1.7
$(element).on('click', function(){ // bind our element with the click
event.view.window.location = element.href; // on click redirect
});
$(element).trigger( 'click' ); // We want to trigger the ^ click event ^
}
Related
On my site, I am trying to fix the navigation so that when the browser is getting resized from desktop to mobile size, the mobile menu works. I have the mobile menu working on initial load, and the desktop navigation working on initial load, but when I run the script in a $(window).on('resize', function() {} and click an item as depicted in my script, the event fires always +1 each time the window was rested after a resize.
What I mean is, if I load the page, scale it into mobile size, click the menu and a dropdown item, the click event will fire once. Resize the window out and then back in, the click event will fire now 2 times, then 3, and so on, depending on how many times the browser was resized.
I'm not sure exactly what is going on in my resize script that is screwing everything up and I'm at my wits end at trying to figure it out. Normally people aren't sitting there resizing their browser from desktop to mobile, but my boss does when he show's clients a beta of their site and wants this to never be an issue.
Here is my resize script:
(function( $ ) {
var id,
$body = $('body'),
$window = $( window ),
$navSlider = $('.nav-slider'),
$navMask = $( '.nav-mask' ),
$navToggler = $( '.navbar-toggler' ),
$parent = $( '.menu-item-has-children' ),
$parentLink = $( '.dropdown-toggle' ),
$childContainer = $( '.dropdown-menu' );
$window.on( 'resize', function( e ) {
clearTimeout(id);
id = setTimeout(function() {
close();
var width = $window.width();
if ( width < 992 ) {
setHeightToNav();
$navMask.on( 'click', function() { close() } );
$navToggler.on( 'click', function() { open() } );
$parentLink.on( 'click', function( e ) {
e.preventDefault();
var $this = $( this );
$this.data( 'clicked', true );
console.log( $this.parent() );
} )
}
if ( width >= 992 ) {
resetNavHeight();
console.clear();
}
}, 500 );
} );
function setHeightToNav() {
if ( $body.hasClass( 'logged-in' ) ) {
var $wpAdminBar = $( '#wpadminbar' ).outerHeight();
$navSlider.css( { top: $wpAdminBar + 'px' } );
}
var $navHeight = $( '#header-container' ).outerHeight();
$navSlider.css( { marginTop: $navHeight + 'px' } );
}
function resetNavHeight() {
if ( $body.hasClass( 'logged-in' ) ) {
$navSlider.css( { top: 0 + 'px' } );
}
$navSlider.css( { marginTop: 0 + 'px' } );
}
function close() {
$body.removeClass( 'has-active-menu' );
setTimeout( function() {
$navSlider.removeClass( 'toggling' );
$parent.removeClass( 'show' );
$parentLink.attr( 'aria-expanded', false );
$childContainer.removeClass( 'show' ).removeAttr( 'style' );
$parentLink.data('clicked', false);
}, 250 );
console.log('close()');
}
function open() {
$body.addClass( 'has-active-menu' );
$navSlider.addClass( 'toggling' );
}
})( jQuery );
I've tried my script both with AND without the setTimeout function and it happens exactly the same.
On the project, we are using Bootstrap 4, with the Bootstraps Dropdown._clearMenus(); function commented out in the right places as it was causing conflicts with the functionality I wanted with the navigation.
A link to a site where you can see this is here. It's a WordPress site as well if that matters for anything.
Any help is appreciated. I've been at this for several hours and am at my wits end.
.on( 'click', function ) does not set the event listener, it adds an event listener. Try doing off('click') before setting it if you really need to set this listener here.
But note that any other 'click' listener for this element will also be removed.
That's for the quick fix. You could do better, but that would require more work (track with a boolean if you just changed "display mode", and add or remove the event listeners only then, for example).
I am writing a JQuery plugin for a project I'm working on which turns from tabbed content on desktop devices to an accordion on mobile devices. I've used JQuery Boilerplate (https://github.com/jquery-boilerplate/jquery-boilerplate/blob/master/dist/jquery.boilerplate.js) as an initial pattern for my plugin.
The plugin is called on any element with the class ".tabs2accordion" as shown here:
$(".tabs2accordion").tabs2Accordion({state:"desktop"});
The plugin works as expected if there is only one element with ".tabs2accordion" class on a page but starts to malfunction as soon as another element with the same class is added to the page. I've created a codepen of the basic code to demo the issue. To show the issue, on a window size of >768px try clicking any of the titles and observe how the content below changes as each title is clicked. Next uncomment the block of HTML and try clicking on the titles again.
http://codepen.io/decodedcreative/pen/MyjpRj
I have tried looping through each element with the class "tabs2accordion" like this:
$(".tabs2accordion").each(function(){
$(this).tabs2Accordion({state:"desktop"});
});
But this didn't fix the issue either.
Any ideas?
I have not used jQuery Boilerplate, but I believe the problem here is with your variable called plugin.
Nowhere in your code do you declare a variable called plugin. When I stop the debugger in Plugin.prototype.showTabContent, I can evaluate window.plugin and it returns the global value for plugin.
In the constructor for Plugin, the first line reads plugin= this;. Since plugin is not defined, it is declaring the variable at global scope on the window object.
The fix is to pass a reference to the plugin object when setting up the $().on() hook. The data passed is available in the event handlers via the event parameter that is passed in the data property.
Here is the solution (at http://codepen.io/shhQuiet/pen/JXEjMV)
(function($, window, document, undefined) {
var pluginName = "tabs2Accordion",
defaults = {
menuSelector: ".tabs2accordion-menu",
tabContentSelector: ".tabs2accordion-content"
};
function Plugin(element, options) {
this.element = element;
this.$element = $(this.element);
this.options = $.extend({}, defaults, options);
this.$menu = $(this.element).find(this.options.menuSelector),
this.$tabs = $(this.element).find(this.options.tabContentSelector),
this.$accordionTriggers = $(this.element).find(this.$tabs).find("h3");
this._defaults = defaults;
this._name = pluginName;
this.init();
}
Plugin.prototype = {
init: function() {
//Set all the tab states to inactive
this.$tabs.attr("data-active", false);
//Set the first tab to active
this.$tabs.first().attr("data-active", true);
//If you click on a tab, show the corresponding content
this.$menu.on("click", "li", this, this.showTabContent);
//Set the dimensions (height) of the plugin
this.resizeTabs2Accordion({
data: this
});
//If the browser resizes, adjust the dimensions (height) of the plugin
$(window).on("resize", this, this.resizeTabs2Accordion);
//Add a loaded class to the plugin which will fade in the plugin's content
this.$element.addClass("loaded");
console.log(this.$element);
},
resizeTabs2Accordion: function(event) {
var contentHeight;
var plugin = event.data;
if (!plugin.$element.is("[data-nested-menu]")) {
contentHeight = plugin.$tabs.filter("[data-active='true']").outerHeight() + plugin.$menu.outerHeight();
} else {
contentHeight = plugin.$tabs.filter("[data-active='true']").outerHeight();
}
plugin.$element.outerHeight(contentHeight);
},
showTabContent: function(event) {
var $target;
var plugin = event.data;
plugin.$menu.children().find("a").filter("[data-active='true']").attr("data-active", false);
plugin.$tabs.filter("[data-active='true']").attr("data-active", false);
$target = $($(this).children("a").attr("href"));
$(this).children("a").attr("data-active", true);
$target.attr("data-active", true);
plugin.resizeTabs2Accordion({data: plugin});
return false;
},
showAccordionContent: function(event) {
var plugin = event.data;
$("[data-active-mobile]").not($(this).parent()).attr("data-active-mobile", false);
if ($(this).parent().attr("data-active-mobile") === "false") {
$(this).parent().attr("data-active-mobile", true);
} else {
$(this).parent().attr("data-active-mobile", false);
}
}
};
$.fn[pluginName] = function(options) {
return this.each(function() {
if (!$.data(this, "plugin_" + pluginName)) {
$.data(this, "plugin_" + pluginName, new Plugin(this, options));
}
});
};
})(jQuery, window, document);
$(window).on("load", function() {
$(".tabs2accordion").tabs2Accordion({
state: "desktop"
});
});
I rewrote your code following jQuery's Plugin creation standard.
http://codepen.io/justinledouxmusique/pen/GZrMgB
Basically, I did two things:
Moved away from using data attributes for styling (switched to using an .active class instead)
Moved away from using this everywhere, as it bring a whole wave of binding issues...
$.fn.tabs2Accordion loops through all the selectors, and applies $.tabs2Accordion. It also returns the selector for chaining (it's a standard in jQuery).
Then, all the internal methods are function expressions which are in the same scope as all your old this "variables". This simplifies the code greatly as you can refer to those variables without passing them in as a parameter or without having to .bind( this ) somehow.
Finally, the old init() function is gone. Instead, I put the code at the end of the $.tabs2Accordion function.
Hope this helps!
(function ( window, $ ) {
$.tabs2Accordion = function ( node, options ) {
var options = $.extend({}, {
menuSelector: '.tabs2accordion-menu',
tabContentSelector: '.tabs2accordion-content'
}, options )
var $element = $( node ),
$menu = $element.find( options.menuSelector ),
$tabs = $element.find( options.tabContentSelector ),
$accordionTriggers = $tabs.find( 'h3' )
var resizeTabs2Accordion = function () {
$element.outerHeight( !$element.is( '[data-nested-menu]' )
? $element.find( 'div.active' ).outerHeight() + $menu.outerHeight()
: $element.find( 'div.active' ).outerHeight() )
}
var showTabContent = function () {
var $this = $( this ) // This will be the clicked element
$menu
.find( '.active' )
.removeClass( 'active' )
$element
.find( '.active' )
.removeClass( 'active' )
$( $this.find( 'a' ).attr( 'href' ) )
.addClass( 'active' )
$this
.find( 'a' )
.addClass( 'active' )
resizeTabs2Accordion()
return false
}
var showAccordionContent = function () {
var $this = $( this ),
$parent = $this.parent(),
mobileIsActive = $parent.data( 'active-mobile' )
$( '[data-active-mobile]' )
.not( $parent )
.data( 'active-mobile', false )
$parent
.data( 'active-mobile', mobileIsActive ? false : true )
}
// The equivalent of init()
$tabs
.removeClass( 'active' )
.first()
.addClass( 'active' )
$element.addClass( 'loaded' )
$menu.on( 'click', 'li', showTabContent )
$( window ).on( 'resize', resizeTabs2Accordion )
resizeTabs2Accordion()
console.log( $element )
}
$.fn.tabs2Accordion = function ( options ) {
this.each( function ( index, node ) {
$.tabs2Accordion( node, options )
})
return this
}
})( window, jQuery )
$( window ).on( 'load', function () {
$( '.tabs2accordion' ).tabs2Accordion({
state: 'desktop'
})
})
I have the following js code:
This code is used on a FAQ toggle page.
It works basically as every toogle code, but I would like to add the auto-close function when clicking to an other question.
Hide the prev question content, then show the next one.
Any ideas?
if ( 'function' !== typeof(window[ 'vc_toggleBehaviour' ] ) ) {
window.vc_toggleBehaviour = function ( $el ) {
function event( e ) {
e && e.preventDefault && e.preventDefault();
var title = jQuery( this );
var element = title.closest( '.vc_toggle' );
var content = element.find( '.vc_toggle_content' );
if ( element.hasClass( 'vc_toggle_active' ) ) {
content.slideUp( {
duration: 300,
complete: function () {
element.removeClass( 'vc_toggle_active' );
}
} );
} else {
content.slideDown( {
duration: 300,
complete: function () {
element.addClass( 'vc_toggle_active' );
}
} );
}
}
if ( $el ) {
if ( $el.hasClass( 'vc_toggle_title' ) ) {
$el.unbind( 'click' ).click( event );
} else {
$el.find( ".vc_toggle_title" ).unbind( 'click' ).click( event );
}
} else {
jQuery( ".vc_toggle_title" ).unbind( 'click' ).on( 'click', event );
}
}
}
Whenever any of the questions are clicked, if you were to hide all active questions you won't have to worry about which one -if any- are currently active.
(on question click):
$('.vc_toggle_active').each(function(){
$(this)slideUp( {
duration: 300,
complete: function () {
$(this).removeClass( 'vc_toggle_active' );
}
});
});
After sliding up any currently active question, go ahead and show the clicked one.
NB. Code not tested, since you have no fiddle and no html. Hope you get the concept though.
So there is some code from codepen.io http://codepen.io/karolpodlesny/pen/npKqu. It is uploaded here: http://fredricarms.com/javatestindex.html.
Now, the HTML, CSS and JavaScript for making the boxes expand and do all the cool things, are working how they are supposed to, being in separate files, so is the modernizr. I also the know that the js is being called because in the boxlayout.js I wrote some code to bring up and alert box and it worked just fine. So I am guessing that codepen fixes the code so it runs perfectly. I just don't know what is wrong with the code in the boxlayout.js that is not working on my server. Please help and thank you so much. Below is the code in the boxlayout js file.
var Boxlayout = (function() {
var $el = $( '#bl-main' ),
$sections = $el.children( 'section' ),
// works section
$sectionWork = $( '#bl-work-section' ),
// work items
$workItems = $( '#bl-work-items > li' ),
// work panels
$workPanelsContainer = $( '#bl-panel-work-items' ),
$workPanels = $workPanelsContainer.children( 'div' ),
totalWorkPanels = $workPanels.length,
// navigating the work panels
$nextWorkItem = $workPanelsContainer.find( 'nav > span.bl-next-work' ),
// if currently navigating the work items
isAnimating = false,
// close work panel trigger
$closeWorkItem = $workPanelsContainer.find( 'nav > span.bl-icon-close' ),
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
},
// transition end event name
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
// support css transitions
supportTransitions = Modernizr.csstransitions;
function init() {
initEvents();
}
function initEvents() {
$sections.each( function() {
var $section = $( this );
// expand the clicked section and scale down the others
$section.on( 'click', function() {
if( !$section.data( 'open' ) ) {
$section.data( 'open', true ).addClass( 'bl-expand bl-expand-top' );
$el.addClass( 'bl-expand-item' );
}
} ).find( 'span.bl-icon-close' ).on( 'click', function() {
// close the expanded section and scale up the others
$section.data( 'open', false ).removeClass( 'bl-expand' ).on( transEndEventName, function( event ) {
if( !$( event.target ).is( 'section' ) ) return false;
$( this ).off( transEndEventName ).removeClass( 'bl-expand-top' );
} );
if( !supportTransitions ) {
$section.removeClass( 'bl-expand-top' );
}
$el.removeClass( 'bl-expand-item' );
return false;
} );
} );
// clicking on a work item: the current section scales down and the respective work panel slides up
$workItems.on( 'click', function( event ) {
// scale down main section
$sectionWork.addClass( 'bl-scale-down' );
// show panel for this work item
$workPanelsContainer.addClass( 'bl-panel-items-show' );
var $panel = $workPanelsContainer.find("[data-panel='" + $( this ).data( 'panel' ) + "']");
currentWorkPanel = $panel.index();
$panel.addClass( 'bl-show-work' );
return false;
} );
// navigating the work items: current work panel scales down and the next work panel slides up
$nextWorkItem.on( 'click', function( event ) {
if( isAnimating ) {
return false;
}
isAnimating = true;
var $currentPanel = $workPanels.eq( currentWorkPanel );
currentWorkPanel = currentWorkPanel < totalWorkPanels - 1 ? currentWorkPanel + 1 : 0;
var $nextPanel = $workPanels.eq( currentWorkPanel );
$currentPanel.removeClass( 'bl-show-work' ).addClass( 'bl-hide-current-work' ).on( transEndEventName, function( event ) {
if( !$( event.target ).is( 'div' ) ) return false;
$( this ).off( transEndEventName ).removeClass( 'bl-hide-current-work' );
isAnimating = false;
} );
if( !supportTransitions ) {
$currentPanel.removeClass( 'bl-hide-current-work' );
isAnimating = false;
}
$nextPanel.addClass( 'bl-show-work' );
return false;
} );
// clicking the work panels close button: the current work panel slides down and the section scales up again
$closeWorkItem.on( 'click', function( event ) {
// scale up main section
$sectionWork.removeClass( 'bl-scale-down' );
$workPanelsContainer.removeClass( 'bl-panel-items-show' );
$workPanels.eq( currentWorkPanel ).removeClass( 'bl-show-work' );
return false;
} );
}
return { init : init };
})();
Your code relies entirely on jQuery, however you haven't included jQuery on your live site. On your CodePen example you're using jQuery 1.9.1:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
You can continue to use the Google-hosted version, or if you want to host it yourself you can download jQuery from jQuery's own website.
The problem is that you need to initialize your BoxLayout once your document has finished loading (all those jQuery variables you're using inside your library won't be defined because their equivalent dom elements are not yet rendered), that's why you need to init everything when dom is ready.
Add this line of code to the bottom of your boxlayout.js file:
$(document).ready(function() {
Boxlayout.init();
});
Just for testing purposes, open up the web developer console on your website, and run this js code:
Boxlayout.init();
You'll see everything will work just fine.
I'm having an issue with multiple jQuery UI dialogs. What I want is that I can have multiple dialogs, and a click on a button with the class ".close-modal" will only close the dialog in which that button was located. Now, first for a little background: I have a simple function in the "main" parent of the site, which will open a modal dialog by clicking on a link with the class ".modal";
In the parent
function modal( href, title ) {
var $dialog = $('<div></div>');
$dialog.html('<iframe class="modal" name="' + title + '" style="border: 0px;" src="' + href + '" width="100%" height="100%"></iframe>');
$dialog.dialog({
autoOpen: false,
modal: true,
height: 650,
width: 950,
title: title
});
$dialog.dialog( 'open' );
$(document).bind( 'close-dialog', function( ) {
$dialog.dialog( 'close' );
});
}
$('a.modal').click( function( event ) {
event.preventDefault( );
modal( $(this).attr( 'href' ), $(this).attr( 'title' ) );
}
This code works the way I want it to. To open a dialog from a dialog, I call the parent window's modal function with the correct parameters, so that multiple dialogs can be displayed within the parent:
In the dialog
( function( $ ) {
$(document).ready( function( ) {
$('a.modal').click( function( event ) {
event.preventDefault( );
parent.modal( $(this).attr( 'href' ), $(this).attr( 'title' ) );
}
/** Trigger the close-dialog event on the parent to close the current dialog. */
$('.close-dialog').click( function( ) {
parent.jQuery( parent.document ).trigger( 'close-dialog' );
});
});
})( jQuery );
So, in essence; I've defined an event on the parent window ("close-dialog"), which can be called by the code within the iframe which will call $dialog.dialog( 'close' ); This works brilliantly; I can create multiple dialogs, even from within dialogs, but the issue is that once I click a button.close-dialog, it closes all of my dialogs.
Is there any way to determine from which dialog the event was called? Or bind the event to the current dialog?
You could use jquery modal's buttons option.
<script>
$(function() {
$( ".myModals" ).dialog({
modal: true,
buttons: {
Close: function() {
$( this ).dialog( "close" );
}
}
});
});
</script>
This should do it for all your modals in one go, and the buttons it creates only apply to themselves. Hope that helps!