Trigger Bootstrap JS behavior for dynamically loaded HTML content - javascript

I am dynamically loading HTML templates containing Bootstrap markup. However, none of the Bootstrap Javascript behavior is being applied to the loaded content. For example, if the loaded content contained markup for the Bootstrap modal the modal doesn't behave correctly.
Is there a way I can trigger Bootstrap to refresh or apply it's Javascript to the newly loaded content?

you'll need to initialize the modal in the callback of whatever function/request object is loading the dynamic content
it's hard to say without seeing your code but something like this
require(['!text/myDynamicTemplate.html'], function(template){
//logic to render the template and/or insert it into the dom here
$('#myModal').modal(options)
})
Edit:
Bootstrap defines its javascript plugins individually. There is no global bootstrap object
Here's the plugin definition for tooltip
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.tooltip')
var options = typeof option == 'object' && option
if (!data && /destroy|hide/.test(option)) return
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.tooltip
$.fn.tooltip = Plugin
$.fn.tooltip.Constructor = Tooltip
// TOOLTIP NO CONFLICT
// ===================
$.fn.tooltip.noConflict = function () {
$.fn.tooltip = old
return this
}
}(jQuery);
Thus the only thing you get access to via $('#myID').tooltip is the constructor and initializer
the best you can do without modifying the bootstrap code
$('[data-toggle=tooltip]').tooltip()

Alert close buttons and model open buttons by default are already delegated. If there's a data-toggle attribute, it's delegated, except for tooltips and popovers. Tooltips and popovers are opt-in. Every other bootstrap js component may have a refresh method, for example the scrollspy script.
I usually add a function in which I apply the needed javascript like bootstrap (or WYSIWIG editors) to any scope.
There is also bootbox which might be helpful to you: http://bootboxjs.com/
As for bootstrap model loading via ajax, http://jschr.github.io/bootstrap-modal/ might be helpfull to you.

It's worth giving this a shot:
http://nakupanda.github.io/bootstrap3-dialog/#loading-remote-page
See more discussions:
https://github.com/nakupanda/bootstrap3-dialog/issues/217
https://github.com/nakupanda/bootstrap3-dialog/issues/189
https://github.com/nakupanda/bootstrap3-dialog/issues/185
https://github.com/nakupanda/bootstrap3-dialog/issues/119
https://github.com/nakupanda/bootstrap3-dialog/issues/44

Related

Custom dynamic popover using pure Javascript

I need to write a pure JavaScript function (setPopOver) as a coding exercise for my college course that receives a title and content and generates popover when hovering over any HTML element. I've worked with JS with Angular before, but no pure JS, so I'm kinda stuck.
The API for using this should be:
$(SOME_HTML_ELEMENT).setPopOver(title, content);
How am I suppose to "inject" a new function to a DOM node to use ?
Any help is appreciated.
If you are using jquery, you can add custom function to jquery object
$.fn.setPopOver = function(title, content) {
$(this).hover(
function(){
// add popup element here
},
function() {
// remove popup element here
}
)
}

Reload foundation all or partially

In my website, the main index.php have a drop-down menu, and if I click on an item, an Ajax loader work and writes in the main div of the main index.
But if this import use foundation function (reveal, callout, tabs..), this import doesn't work.
If in web explorer console I insert again this command '$(document).foundation();', import works but I receive in the log a warning
Tried to initialize drill down on an element that already has a Foundation plugin.
foundation.min.js:65 Tried to initialize dropdown-menu on an element that already has a Foundation plugin.foundation.min.js:65
Tried to initialize off-canvas on an element that already has a Foundation plugin.foundation.min.js:65
Tried to initialize responsive-toggle on an element that already has a Foundation plugin.foundation.min.js:65
Tried to initialize reveal on an element that already has a Foundation plugin.
Can somebody help me to find a clean solution to reload foundation?
In Foundation 6.2.3,
$(document).foundation()
will call
reflow: function(this, plugins)
with plugins undefined, so every plugin will be checked: The function will look at each child of $(document) with a [data-plugin_name] attribute and show this warning if that element already has been initialized:
if ($el.data('zfPlugin')) {
console.warn("Tried to initialize " + name + " on an element that already has a Foundation plugin.");
return;
}
So you can either call foundation() directly on the new element after your ajax request :
$('#your_element').foundation();
or call reflow with a specified plugin (eg. reveal)
Foundation.reflow($('document'), 'reveal')
provided you don't already have an initialized 'reveal' element in your DOM
Mixed: (best solution I think)
Foundation.reflow($('#your_element'), 'reveal');
Firstly, do not call $(document).foundation(); more than once.
If you've updated an element already initialized with a plugin, you'll need to use reInit.
If you've added or completely replaced an element with a new element you'll need to use $(element).foundation();
I use the following technique:
First, when foundation is initialized I store a global:
$(document).foundation();
__foundationRun = true;
Then, no matter when, the following util will handle initialization of new/updated elements:
function foundationUpdate(el) {
if (__foundationRun) {
if (el.data('zfPlugin'))
// existing element already has a plugin - update
Foundation.reInit(el);
else
// new element - initialize
el.foundation();
}
// else leave for foundation to init
}
So you could use like this:
$(".accordion").append("<li>Item</li>");
foundationUpdate($(".accordion"));
or
var newAccordion = $("<ul class='vertical menu accordion-menu'"
+ " data-accordion-menu><li>Item</li></ul>");
$(".container").append(newAccordion);
foundationUpdate(newAccordion);

JQuery Mobile Custom Flipbox set initialization value

I have a custom flipbox which is described in the accepted answer here: JQuery Mobile Custom Flipbox get selected value.
I'm struggling to set the initial value after the page is rendered my 'change-page' function is something like this:
changePage:function (page) {
page.$el.attr('data-role', 'page');
page.render();
$('body').append(page.$el).trigger('create');
$.mobile.changePage(page.$el, {changeHash:false});
}
As I use Backbone.js to manage the views and then I delegate the navigation to jQM.
I basically need to set the initial value of this input text field ( maybe there's a workaround)
Ok I figured this out myself and I'm willing to share the solution:
first of all the change page function is slightly different:
changePage:function (page) {
page.$el.attr('data-role', 'page');
//get rid of everything in the old page.
page.remove();
//render the page again
page.render();
$('body').append(page.$el).trigger('create');
$.mobile.changePage(page.$el, {changeHash:false});
}
the remove call is necessary to get rid of every event listener you had in the old rendered HTML.
In every page that needs init parameters you can specify in the render function this:
render: function(){
....
this.$el.on('pageshow',this.initFields);
....
}
initFields: function(){
// put your jQuery code here (e.g. this.$el.find('foo').val('initValue');
}
please note that this solution as of the jQM documentation is valid up to the 1.5.0 version of jQM, after that the pageshow event will not be triggered anymore.

CKEditor widget: how do I attach css in onLoad

I'm writing a CKEditor widget. As I am trying to make it easily distributable, I want to make my css external. While this syntax works:
CKEDITOR.plugins.add('myplugin', {
requires: 'widget',
icons: 'myplugin',
init: function(editor) {
CKEDITOR.dialog.add('myplugin', this.path + 'dialogs/myplugin.js');
var self = this;
editor.on('contentDom', function() {
editor.document.appendStyleSheet(CKEDITOR.getUrl(self.path + 'css/style.css'));
});
}
});
I found that CKSource uses onLoad: function(editor){} in their image2 widget to attach css. This looks more canonical than my code. I'm getting editor = undefined though. What's the catch?
Update:
I'm using the framed option and I definitely want to stay DRY through .appendStyleSheet() to user-facing page as well as to the wysiwyg framed editor. However, I still have problem with init/beforeInit:function(editor), where editor does not seem to have document property populated at that very moment. When I do window.ed = editor though, then I can access the window.ed.document in the console once the page is ready. So, is there any kind of deferred promise going on there? Maybe I'm complicating things, but I definitely want to append a distributable stylesheet and remove it easily.
I found this working:
beforeInit: function(
var ccss = CKEDITOR.config.contentsCss;
if(typeof ccss == 'string'){
ccss = [ccss];
}
ccss.push('/css/style.css'); // <-- my css installed at website root
CKEDITOR.config.contentsCss = ccss;
console.log(CKEDITOR.config.contentsCss);
},
While the issue with isolated css stylesheet might sound over-complicated, I want to achieve an easily demoable state without the need to touch existing files.
Note that in the image2 plugin stylesheet is not added to editor instance, but using CKEDITOR.addCss:
CKEDITOR.plugins.add( 'image2', {
...
onLoad: function( editor ) {
CKEDITOR.addCss( ' ... ' );
}
} );
It is a mistake that the editor is specified as an argument - it always will be null.
If you want to add stylesheet manually using document.appendStyleSheet, then do it on init, or beforeInit, not on onLoad.
Altough, you should know that contentDom will be fired multiple times for one document, e.g. when setting data. So in your case stylesheet would be added many times. Therefore, if you want to add a stylesheet for the framed editor (the one using iframe), you can use config.contentsCss, and if you want to add it for inline editor, then you can freely call:
CKEDITOR.document.appendStyleSheet( ... );
In plugin's onLoad (will be called only once). So most likely you'll want to do both things, to handle inline and framed editors.

How do we set an onExpand event in a cfLayout accordion

We're using CFLayout to create a tab structure in our web application. After creation of that layout we call this function:
mytabs = ColdFusion.Layout.getTabLayout("#attributes.cflayoutName#");
mytabs.on('tabchange',
function(tablayout,tab) {
var tabtitle = tab.title;
alert(tabtitle); // Actual code does various useful 'stuff' here.
}
);
That piece of code works very well, and the alert will show each time the user clicks on a tab.
The problem is that we are now trying to do the same thing with a CFLayout type of "accordion", and I cannot get an event to fire when the user switches which accordion pane they are looking at. We've tried leaving the above as is, as well as changing the "tabchange" attribute to "expand", "beforeexpand", "activate", and "collapse".
For this testing I'm using the following simple JS function to avoid issues arising from the JS within the onchange event:
mytabs = ColdFusion.Layout.getAccordionLayout("#attributes.cflayoutName#");
mytabs.on('expand',
function(tablayout,tab) {
console.log('test');
}
);
We do not receive any errors. Nothing is logged to the console at all. I've tried replacing the console.log to an alert to rule out any problems with that line.
I found that the Ext library documentation to be very helpful with finding a solution to this problem: here.
The Ext library has a getComponent method that allows you to reference the accordion layout panel that you are trying to add the expand event to. Once you have this, you can use the "on" method you are using above to assign the expand event to each panel individually.
for (x=1; x<accordionLayoutArray.length; x++) {
mytabs.getComponent(accordionPanelName).on('expand',
function(tab) { ... });
}
This became too long for a comment so adding as an answer
After some Google searches I found what I think are some related posts. It appears as though the accordion in Ext JS does not have the same events as the tab. Instead you need to add a listener in order to catch the expanding.
See this post - in case something happens to that page here is the relevant piece:
You'd need to listen to the expand event of the child panels in the accordion, you could do something like:
Code:
myAccordion.add(myFunc('myTitle'));
function myFunc(title)
{
return new Ext.Panel(
{
title: title,
listeners: { 'expand': {fn: something, scope: foo}}
}
);
}
And I also found another similar post here on SO - see both answers
Once you know that the accordion needs a listener you can find a number of results on Google. Such as How do I attach an event handler to a panel in extJS?
This Google search will give you lots of examples.
Hope that helps.

Categories