I have the following script that's used to create a modal with buttons. I just have the first lines below:
$.modal = function(options)
{
var settings = $.extend({}, $.modal.defaults, options),
root = getModalDiv(),
// Vars for resizeFunc and moveFunc
winX = 0,
winY = 0,
contentWidth = 0,
contentHeight = 0,
mouseX = 0,
mouseY = 0,
resized, content = '', contentObj;
and then later:
var buttonsFooter = false;
$.each(settings.buttons, function(key, value)
{
// Button zone
if (!buttonsFooter)
{
buttonsFooter = $('<div class="block-footer align-'+settings.buttonsAlign+'"></div>').insertAfter(contentBlockWrapper);
}
else
{
// Spacing
buttonsFooter.append(' ');
}
Here's the way I create the modal:
$.modal({
title: title,
closeButton: true,
content: content,
complete: function () {
applyTemplateSetup();
$($('#main-form')).updateTabs();
},
width: 900,
resizeOnLoad: true,
buttons: {
'Submit' : function () {
formSubmitHandler($('#main-form'));
}
}
});
What I would like to do is to use this script to create my modal with buttons and dynamically assign the button name. However when I tried replacing 'Submit' with the name of a javascript variable it didn't work. Does anyone have any idea how I could do what I need?
The reason replacing 'Submit' with a variable name doesn't work is because the quotes aren't needed when defining an object in that manner (unless the name contains some special characters). var obj = { 'Submit': function() { } } is exactly the same thing as var obj = { Submit: function() { } }.
You'll notice that you're already omitting the quotes like this in the object you're passing to $.modal().
However, while you can't write
var myVariable = 'abc';
var obj = { myVariable: 3 };
... and expect obj.abc to exist (obj.myVariable will exist), you can write
var myVariable = 'abc';
var obj[myVariable] = 3;
... and obj.abc will exist.
Thus, you could solve your problem in the following manner:
var buttons = { };
buttons[variableName] = function () {
formSubmitHandler($('#main-form'));
};
$.modal({
...
buttons: buttons
});
Edit: David Hedlund beat me to answering, and offered a similar answer which involves a little less code modification. He suggests only breaking out the buttons options instead of the entire options var. End Edit
First, break the options out into a variable:
var modalOpts = {
title: title,
closeButton: true,
content: content,
complete: function () {
applyTemplateSetup();
$($('#main-form')).updateTabs();
},
width: 900,
resizeOnLoad: true,
buttons: {
'Submit' : function () {
formSubmitHandler($('#main-form'));
}
}
};
$.modal(modalOpts);
Then break the button in question out of the options, and use square bracket notation to access and write the property:
var dynamicButtonName = 'Submit'; //Or whatever
var modalOpts = {
title: title,
closeButton: true,
content: content,
complete: function () {
applyTemplateSetup();
$($('#main-form')).updateTabs();
},
width: 900,
resizeOnLoad: true,
buttons: {}
};
modalOpts.buttons[dynamicButtonName] = function () {
formSubmitHandler($('#main-form'));
};
$.modal(modalOpts);
Related
I am writing my first jQuery plugin which is a tree browser. It shall first show the top level elements and on click go deeper and show (depending on level) the children in a different way.
I got this up and running already. But now I want to implement a "back" functionality and for this I need to store an array of clicked elements for each instance of the tree browser (if multiple are on the page).
I know that I can put instance private variables with "this." in the plugin.
But if I assign an event handler of the onClick on a topic, how do I get this instance private variable? $(this) is referencing the clicked element at this moment.
Could please anyone give me an advise or a link to a tutorial how to get this done?
I only found tutorial for instance specific variables without event handlers involved.
Any help is appreciated.
Thanks in advance.
UPDATE: I cleaned out the huge code generation and kept the logical structure. This is my code:
(function ($) {
$.fn.myTreeBrowser = function (options) {
clickedElements = [];
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
titleAttribute: "Title",
idAttribute: "Id",
parentIdAttribute: "ParentId",
levelAttribute: "Level",
treeData: {}
};
var opts = $.extend({}, $.fn.myTreeBrowser.defaults, options);
function getTreeData(id) {
if (opts.data) {
$.ajax(opts.data, { async: false, data: { Id: id } }).success(function (resultdata) {
opts.treeData = resultdata;
});
}
}
function onClick() {
var id = $(this).attr('data-id');
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, id);
}
function handleOnClick(parentContainer, id) {
if (opts.onTopicClicked) {
opts.onTopicClicked(id);
}
clickedElements.push(id);
if (id) {
var clickedElement = $.grep(opts.treeData, function (n, i) { return n[opts.idAttribute] === id })[0];
switch (clickedElement[opts.levelAttribute]) {
case 1:
renderLevel2(parentContainer, clickedElement);
break;
case 3:
renderLevel3(parentContainer, clickedElement);
break;
default:
debug('invalid level element clicked');
}
} else {
renderTopLevel(parentContainer);
}
}
function getParentContainer(elem) {
return $(elem).parents('div.myBrowserContainer').parents()[0];
}
function onBackButtonClick() {
clickedElements.pop(); // remove actual element to get the one before
var lastClickedId = clickedElements.pop();
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, lastClickedId);
}
function renderLevel2(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderLevel3(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderTopLevel(parentContainer) {
parentContainer.html('');
var browsercontainer = $('<div>').addClass('fct-page-pa fct-bs-container-fluid pexPAs myBrowserContainer').appendTo(parentContainer);
// rendering the top level display
}
getTreeData();
//top level rendering! Lower levels are rendered in event handlers.
$(this).each(function () {
renderTopLevel($(this));
});
return this;
};
// Private function for debugging.
function debug(debugText) {
if (window.console && window.console.log) {
window.console.log(debugText);
}
};
}(jQuery));
Just use one more class variable and pass this to it. Usually I call it self. So var self = this; in constructor of your plugin Class and you are good to go.
Object oriented way:
function YourPlugin(){
var self = this;
}
YourPlugin.prototype = {
constructor: YourPlugin,
clickHandler: function(){
// here the self works
}
}
Check this Fiddle
Or simple way of passing data to eventHandler:
$( "#foo" ).bind( "click", {
self: this
}, function( event ) {
alert( event.data.self);
});
You could use the jQuery proxy function:
$(yourElement).bind("click", $.proxy(this.yourFunction, this));
You can then use this in yourFunction as the this in your plugin.
I am using selectize for an auto suggest field, and i have used one of the no_results plugins to show a link when there is no result, and this works great for the most part, but i have a few dramas i am just not sure how to get around
I have two things i need to get help with
1st most important - How to pass variables to the plugin
I have multiple instances of selectize on the several pages, so i need to pass the vars hr_link and hr_label to the plugin so i don't have to recreate the plugin 30 times with just the those vars different
2nd - Allow link to be clicked, bypassing default behaviour
To get the links to be clickable i have used the onmousedown() and touchstart() but is there a better way to re-enable the default click on just this link in the results box.
I have spent a lot of time researching these items, so I don't think it is a duplicate
// The Plugin
Selectize.define('header_no_results', function( options ) {
var KEY_LEFT = 37;
var KEY_UP = 38;
var KEY_RIGHT = 39;
var KEY_DOWN = 40;
var ignoreKeys = [KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN];
var self = this;
var hr_link = 'http://link_to_info.com';
var hr_label = 'country';
options = $.extend({
message: ' No results found: click here to add a'+hr_label,
html: function(data) {
return '<div class="selectize-dropdown-content">' + data.message + '</div>';
}
}, options );
self.on('type', function() {
var message = 'Not Found: click here Add a '+hr_label;
if (!self.hasOptions) {
self.$empty_results_container.html(message).show();
} else {
self.$empty_results_container.hide();
}
});
self.onKeyUp = (function() {
var original = self.onKeyUp;
return function ( e ) {
if (ignoreKeys.indexOf(e.keyCode) > -1) return;
self.isOpen = false;
original.apply( self, arguments );
}
})();
self.onBlur = (function () {
var original = self.onBlur;
return function () {
original.apply( self, arguments );
self.$empty_results_container.hide();
};
})();
self.setup = (function() {
var original = self.setup;
return function() {
original.apply( self, arguments);
self.$empty_results_container = $(
options.html($.extend({
classNames: self.$input.attr( 'class' )
}, options))
);
self.$empty_results_container.hide();
self.$dropdown.append(self.$empty_results_container);
};
})();
});
// the function calling the plugin
$('#companyLinks').selectize({
valueField: 'id',
labelField: 'display',
searchField: 'display',
maxItems: 1,
options: [],
create: false,
onItemAdd: function(value){
window.location.href = 'http://my_link.com/'+value;
},
load: function(query, callback) {
if (!query.length) return callback();
$.ajax({
url: 'http://link.com/get/list',
type: 'GET',
dataType: 'json',
data: {
q: query
},
error: function() {
callback();
},
success: function(res) {
callback(res);
//window.open($(res).val(), '_self');
}
});
},
plugins: ['header_no_results']
});
The solution to pass the var was not all that hard after all just had to look in the right place, and there is further info here
In the function we need to change out
plugins: ['header_no_results']
with
plugins: { "header_no_results": {
link : "page/location",
} }
then we can retrieve link and declare the var we needed in the plugin by
var hr_link = options.link;
I have written some jQuery that will morph a button into a container when clicked.
I now want to use the same script but on a different button. I am not sure the best way to do this though. Would it be to copy and paste the whole morphObject and then change the bits which need changing for the new button, and then run both the object's init functions?
Here is the Fiddle:
https://jsfiddle.net/2a3rp590/
Here is the jQuery:
$(document).ready(function() {
var morphObject = {
button: $('button.morphButton'),
container: $('div.morphContainer'),
overlay: $('div.overlay'),
content: $('h1.content, p.content'),
endPosition: {
top: 100,
left: '50%',
width: 600,
height: 400,
marginLeft: -300
},
init: function() {
var mO = morphObject,
button = mO.button;
button.on('click', function() {
button.fadeOut(200);
setTimeout(mO.containerMove, 200);
});
},
containerMove: function() {
var mO = morphObject,
content = mO.content,
overlay = mO.overlay,
container = mO.container,
span = $('span.close');
overlay.fadeIn();
container.animate(mO.endPosition, 400, function() {
content.fadeIn();
span.fadeIn();
mO.close();
});
},
close: function() {
var mO = morphObject,
container = mO.container,
overlay = mO.overlay,
content = mO.content;
if ( container.find('span.close').length ) return;
$('<span class="close">X</span>').appendTo(container);
var span = $('span.close');
overlay.add(span).on('click', function() {
content.fadeOut();
span.fadeOut();
overlay.fadeOut();
setTimeout(mO.animateBack, 200);
});
},
animateBack: function() {
var mO = morphObject,
container = mO.container;
button = mO.button;
container.animate(mO.startPosition, 400, function() {
button.fadeIn(300);
});
}
}
var container = morphObject.container;
morphObject.startPosition = {
top: container.css('top'),
left: container.css('left'),
width: container.css('width'),
height: container.css('height'),
marginLeft: container.css('margin-left')
};
morphObject.init();
});
You can see in the fiddle, I have added a new button, container and content. How can I make my code work with multiple buttons?
Thanks.
As i said in my comments, you could do the following. Bear in mind that this is an untested version.
init: function(params) {
//object reference
var self = this;
//reassign the button object if it was passed
//else use the default from the object
self.button = typeof params !== "undefined" && params.button || self.button;
self.button.on('click', function() {
$(this).fadeOut(200);
setTimeout(self.containerMove, 200);
});
}
//I am using an object as you my want to
//pass in more options later, just in case
//such as
/*
morphObject.init({
button: $('button.morphButton2'),
container: $('.another-container')
});
*/
morphObject.init({
button: $('button.morphButton2')
});
Or you could have a common class to all buttons that would trigger the click inside the init method. Easy enough?
var morphObject = {
button: $('button.morphButton') //let's say 'morphButton' is the common class
.....
.....
init: function(params) {
//object reference
var self = this;
self.button.on('click', function() {
$(this).fadeOut(200);
setTimeout(self.containerMove, 200);
});
}
.....
.....
.....
}
I'm trying to trigger a callback to dynamically populate a CKEditor dialog with checkboxes when the dialog is opened. I've read other solutions that use iframes, but this won't work for me because the dialog needs to be populated based on other elements on the same page.
Here is what I have so far. There are no errors, but the dialog is just empty when it opens. I expect the addContents function to fill in the dialog. I've confirmed that dialog.definition.contents does include the contents and elements that I want, but it's just not filling in the actual dialog. What am I missing?
(function() {
CKEDITOR.plugins.add( 'embeds', {
icons: 'embed',
init: function(editor) {
var self = this,
elements = [];
CKEDITOR.dialog.add('EmbedsDialog', function (instance) {
return {
title : 'Embeds',
minWidth : 550,
minHeight : 200,
contents: [],
onShow: function() {
var dialog = this,
elements = [];
$('#embeds-fields tr').each(function() {
var title = $(this).find('input[type=text]').val(),
url = $(this).find('input[type=url]').val();
if(url != "") {
elements.push({
label : "embed",
title : url,
type : 'checkbox'
});
}
});
dialog.definition.removeContents('embeds');
dialog.definition.addContents({
id : 'embeds',
expand : true,
elements : elements
});
},
}; // return
});
editor.addCommand('Embeds',
new CKEDITOR.dialogCommand('EmbedsDialog', {
allowedContent: 'a[*](*)'
})
);
editor.ui.addButton('Embeds', {
label : 'Embeds',
command : 'Embeds',
toolbar : 'embeds'
});
} // init
}); // add
})(); // closure
Based off of this example, I ended up with this solution, where "main" is the ID of the original content.
CKEDITOR.on('dialogDefinition', function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if (dialogName == 'EmbedsDialog') {
var main = dialogDefinition.getContents('main');
$('#embeds-fields tr').each(function() {
var title = $(this).find('input[type=text]').val(),
url = $(this).find('input[type=url]').val();
if(url != "") {
main.add({
type : 'checkbox',
label : title,
});
}
});
}
});
I have some html to popup a jQuery UI modal:
Click me
With this javascript code:
$('.popup').click(function() {
var a = this;
var dialog = $('<div>').load($(a).attr('href'), function() {
var form = dialog.find('form');
dialog.dialog({
modal: true,
title: $(a).attr('title'),
buttons: {
Add : function () {
$.post($(form).attr('action'), $(form).serialize());
dialog.dialog('close');
},
Cancel: function () {
dialog.dialog('close');
}
}
});
if ($(a).attr('data-third')) {
// Add here a button
}
});
return false;
});
The idea is the resource in the modal contains a form, on submit of the modal the form is submitted. Now when Click me exists, it means a third button is placed in the modal in this order: Cancel | Add | Add & new.
I tried this code:
if($(a).attr('data-third')) {
var buttons = dialog.dialog('option', 'buttons');
buttons[$(a).attr('data-third')] = function(){
// Callback here
}
dialog.dialog('option', 'buttons', buttons);
}
I got a new button, but the order is Add & new | Cancel | Add. How can I make sure the order is Cancel | Add | Add & new?
With this code you should always get Cancel | Add | Add & new? (from left to right.
$('.popup').click(function() {
var a = this;
var dialog = $('<div>').load($(a).attr('href'), function() {
var form = dialog.find('form');
dialog.dialog({
modal: true,
title: $(a).attr('title'),
buttons: {
Cancel: function() {
dialog.dialog('close');
},
Add: function() {
$.post($(form).attr('action'), $(form).serialize());
dialog.dialog('close');
}
}
});
if ($(a).data('third')) {
var buttons = dialog.dialog('option', 'buttons');
console.log(buttons);
buttons[$(a).data('third')] = function() {
// Callback here
}
dialog.dialog('option', 'buttons', buttons);
}
});
return false;
});
Fiddle here: http://jsfiddle.net/rqq2p/1/
Remember to use data() and not attrib() to get the attribute from the link!
EDIT - basically "buttons" is an object, and each property is one of the buttons. I think that the order in which the buttons appear is the order in which the properties are added. In my example Cancel is the leftmost button because it was the first added property and add & new it's the rightmost because it was the latest. If you need to fiddle with the order you could create a new object and add the properties in the order you want:
if ($(a).data('third')) {
var buttons = dialog.dialog('option', 'buttons');
var newButtons = {};
newButtons[$(a).data('third')] = function() {
// Callback here
}
newButtons['Cancel'] = buttons['Cancel'];
newButtons['Add'] = buttons['Add'];
dialog.dialog('option', 'buttons', newButtons);//now add & new should be the leftmost
}
Untested, but I would imagine the array key should be buttons.length like this
if($(a).attr('data-third')) {
var buttons = dialog.dialog('option', 'buttons');
buttons[buttons.length] = function(){
// Callback here
}
dialog.dialog('option', 'buttons', buttons);
}