AJAX content in a jQuery UI Tooltip Widget - ajax

There is a new Tooltip Widget in jQuery UI 1.9, whose API docs hint that AJAX content can be displayed in it, but without any further details. I guess I can accomplish something like that with a synchronous and blocking request, but this isn't what I want.
How do I make it display any content that was retrieved with an asynchronous AJAX request?

Here is a ajax example of jqueryui tootip widget from my blog.hope it helps.
$(document).tooltip({
items:'.tooltip',
tooltipClass:'preview-tip',
position: { my: "left+15 top", at: "right center" },
content:function(callback) {
$.get('preview.php', {
id:id
}, function(data) {
callback(data); //**call the callback function to return the value**
});
},
});

This isn't a complete solution obviously, but it shows the basic technique of getting data dynamically during the open event:
$('#tippy').tooltip({
content: '... waiting on ajax ...',
open: function(evt, ui) {
var elem = $(this);
$.ajax('/echo/html').always(function() {
elem.tooltip('option', 'content', 'Ajax call complete');
});
}
});
See the Fiddle

One thing to lookout for when using the tooltip "content" option to "AJAX" the text into the tooltip, is that the text retrieval introduces a delay into the tooltip initialization.
In the event that the mouse moves quickly across the tooltip-ed dom node, the mouse-out event may occur before the initialization has completed, in which case the tooltip isn't yet listening for the event.
The result is that the tooltip is displayed and does not close until the mouse is moved back over the node and out again.
Whilst it incurs some network overhead that may not be required, consider retrieving tooltip text prior to configuring the tooltip.
In my application, I use my own jquery extensions to make the AJAX call, parse the resutls and initialise ALL tooltips, obviously you can use jquery and/or your own extensions but the gist of it is:
Use image tags as tooltip anchors, the text to be retrieved is identified by the name atrribute:
<img class="tooltipclassname" name="tooltipIdentifier" />
Use invoke extension method to configure all tooltips:
$(".tooltipclassname").extension("tooltip");
Inside the extension's tooltip method:
var ids = "";
var nodes = this;
// Collect all tooltip identifiers into a comma separated string
this.each(function() {
ids = ids + $(this).attr("name") + ",";
});
// Use extension method to call server
$().extension("invoke",
{
// Model and method identify a server class/method to retrieve the tip texts
"model": "ToolTips",
"method": "Get",
// Send tooltipIds parameter
"parms": [ new myParmClass("tipIds", ids ) ],
// Function to call on success. data is a JSON object that my extension builds
// from the server's response
"successFn": function(msg, data) {
$(nodes).each(function(){
// Configure each tooltip:
// - set image source
// - set image title (getstring is my extension method to pull a string from the JSON object, remember that the image's name attribute identifies the text)
// - initialise the tooltip
$(this).attr("src", "images/tooltip.png")
.prop("title", $(data).extension("getstring", $(this).attr("name")))
.tooltip();
});
},
"errorFn": function(msg, data) {
// Do stuff
}
});
// Return the jquery object
return this;

Here is an example that uses the jsfiddle "/echo/html/" AJAX call with a jQuery UI tooltip.
HTML:
<body>
<input id="tooltip" title="tooltip here" value="place mouse here">
</body>
JavaScript:
// (1) Define HTML string to be echo'ed by dummy AJAX API
var html_data = "<b>I am a tooltip</b>";
// (2) Attach tooltip functionality to element with id == tooltip
// (3) Bind results of AJAX call to the tooltip
// (4) Specify items: "*" because only the element with id == tooltip will be matched
$( "#tooltip" ).tooltip({
content: function( response ) {
$.ajax({
url: "/echo/html/",
data: {
'html': html_data
},
type: "POST"
})
.then(function( data ) {
response( data );
});
},
items: "*"
});
here is this example on jsfiddle:

Related

CKEditor: multiple widget templates

I'm currently creating a 'smartobject' widget. In the widgets dialog, the user can choose a 'smartobject', which simply put, generates some html, which should be added to the editor. Here comes the tricky part: the html sometimes div elements and sometimes simply span elements. In the case of the div variant, the widget should be wrapped in a div 'template'. In the case of a span variant, the widget should be wrapped in a span and the html should be added 'inline'.
In the widgets API I see the following way to define a template:
editor.widgets.add('smartobject', {
dialog: 'smartobject',
pathName: lang.pathName,
template: '<div class="cke_smartobject"></div>', // <------
upcast: function(element) {
return element.hasClass('smartObject');
},
init: function() {
this.setData('editorHtml', this.element.getOuterHtml());
},
data: function() {
var editorHtml = this.data.editorHtml;
var newElement = new CKEDITOR.dom.element.createFromHtml(editorHtml);
newElement.copyAttributes(this.element);
this.element.setText(newElement.getText());
}
});
But in my case, the template is more dynamic: sometimes a div and sometimes the span will do the correct thing..
How can I fix this without needing to create two widgets which will do the exact same thing, with only the wrapping element as difference?
I've already tried to replace the entire element in the 'data' method, like:
newElement.replace(this.element);
this.element = newElement;
But this seemed not supported: resulted in undefined errors after calling editor.getData().
I'm using ckeditor v4.5.9
Thanks for your help!
It seems I got it working (with a workaround).
The code:
CKEDITOR.dialog.add('smartobject', this.path + 'dialogs/smartobject.js');
editor.widgets.add('smartobject', {
pathName: lang.pathName,
// This template is needed, to activate the widget logic, but does nothing.
// The entire widgets html is defined and created in the dialog.
template: '<div class="cke_smartobject"></div>',
init: function() {
var widget = this;
widget.on('doubleclick', function(evt) {
editor.execCommand('smartobject');
}, null, null, 5);
},
upcast: function(element) {
return element.hasClass('smartObject');
}
});
// Add a custom command, instead of using the default widget command,
// otherwise multiple smartobject variants (div / span / img) are not supported.
editor.addCommand('smartobject', new CKEDITOR.dialogCommand('smartobject'));
editor.ui.addButton && editor.ui.addButton('CreateSmartobject', {
label: lang.toolbar,
command: 'smartobject',
toolbar: 'insert,5',
icon: 'smartobject'
});
And in the dialog, to insert code looks like:
return {
title: lang.title,
minWidth: 300,
minHeight: 80,
onOk: function() {
var element = CKEDITOR.dom.element.createFromHtml(smartobjectEditorHtml);
editor.insertElement(element);
// Trigge the setData method, so the widget html is transformed,
// to an actual widget!
editor.setData(editor.getData());
},
...etc.
UPDATE
I made the 'onOk' method a little bit better: the smartobject element is now selected after the insertion.
onOk: function() {
var element = CKEDITOR.dom.element.createFromHtml(smartobjectEditorHtml);
var elementId = "ckeditor-element-" + element.getUniqueId();
element.setAttribute("id", elementId);
editor.insertElement(element);
// Trigger the setData method, so the widget html is transformed,
// to an actual widget!
editor.setData(editor.getData());
// Get the element 'fresh' by it's ID, because the setData method,
// makes the element change into a widget, and thats the element which should be selected,
// after adding.
var refreshedElement = CKEDITOR.document.getById(elementId);
var widgetWrapperElement = CKEDITOR.document.getById(elementId).getParent();
// Better safe then sorry: if the fresh element doesn't have a parent, simply select the element itself.
var elementToSelect = widgetWrapperElement != null ? widgetWrapperElement : refreshedElement;
// Normally the 'insertElement' makes sure the inserted element is selected,
// but because we call the setData method (to ensure the element is transformed to a widget)
// the selection is cleared and the cursor points to the start of the editor.
editor.getSelection().selectElement(elementToSelect);
},
So in short, I partially used the widget API for the parts I wanted:
- Make the html of the widget not editable
- Make it moveable
But I created a custom dialog command, which simply bypasses the default widget insertion, so I can entirely decide my own html structure for the widget.
All seems to work like this.
Any suggestions, to make it better are appreciated:)!
As suggested in this ckeditor forum thread, the best approach would be to set the template to include all possible content elements. Then, in the data function, remove the unnecessary parts according to your specific logic.

Ajax URL # isn't updating

I have a little problem with my script here. For some reason, it doesn't enable the #-tags and I don't know why. I created this javascript using the help of this tutorial. (The loading of the pages works well with no problems at all.)
Could someone please look it over and tell me why it doesn't work?
var default_content="";
$(document).ready(function(){ //executed after the page has loaded
checkURL(); //check if the URL has a reference to a page and load it
$('ul li a').click(function (e){ //traverse through all our navigation links..
checkURL(this.hash); //.. and assign them a new onclick event, using their own hash as a parameter (#page1 for example)
});
setInterval("checkURL()",250); //check for a change in the URL every 250 ms to detect if the history buttons have been used
});
var lasturl=""; //here we store the current URL hash
function checkURL(hash)
{
if(!hash) hash=window.location.hash; //if no parameter is provided, use the hash value from the current address
if(hash != lasturl) // if the hash value has changed
{
lasturl=hash; //update the current hash
loadPage(hash); // and load the new page
}
}
function loadPage(url) //the function that loads pages via AJAX
{
// Instead of stripping off #page, only
// strip off the # to use the rest of the URL
url=url.replace('#','');
$('#loading').css('visibility','visible'); //show the rotating gif animation
$.ajax({
type: "POST",
url: "load_page.php",
data: 'page='+url,
dataType: "html",
success: function(msg){
if(parseInt(msg)!=0) //if no errors
{
$('#content').html(msg); //load the returned html into pageContet
} $('#loading').css('visibility','hidden');//and hide the rotating gif
}
});
}
You can simplify this immensely by adding a function to listen to the hashchange event, like this:
$(window).on("hashchange", function() {
loadPage(window.location.hash);
});
This way you don't need to deal with timers or overriding click events on anchors.
You also don't need to keep track of lasthash since the hashchange even will only fire when the hash changes.

How to use AJAX as an alternative to iframe

I'm trying to put together a snappy webapp, utilizing JS, Prototype and AJAX for all my requests once the GUI has loaded. The app is simple: A set of links and a container element to display whatever the links point to, just like an iframe. Here's an approximate HTML snippet:
<a class="ajax" href="/somearticle.html">An article</a>
<a class="ajax" href="/anotherarticle.html">Another article</a>
<a class="ajax" href="/someform.html">Some form</a>
<div id="ajax-container"></div>
The JS that accompanies the above (sorry it's a bit lengthy) looks like this:
document.observe('dom:loaded', function(event) {
ajaxifyLinks(document.documentElement);
ajaxifyForms(document.documentElement);
});
function ajaxifyLinks(container) {
container.select('a.ajax').each(function(link) {
link.observe('click', function(event) {
event.stop();
new Ajax.Updater($('ajax-container'), link.href, {
method: 'get',
onSuccess: function(transport) {
// Make sure new ajax-able elements are ajaxified
ajaxifyLinks(container);
ajaxifyForms(container);
}
});
});
});
}
function ajaxifyForms(container) {
console.debug('Notice me');
container.select('form.ajax').each(function(form) {
form.observe('submit', function(event) {
event.stop();
form.request({
onSuccess: function(transport) {
$('ajax-container').update(transport.responseText);
// Make sure new ajax-able elements are ajaxified
ajaxifyLinks(container);
ajaxifyForms(container);
}
});
});
});
}
When clicking a link, the response is displayed in the container. I'm not using an iframe for the container here, because I want whatever elements are on the page to be able to communicate with each other through JS at some point. Now, there is one big problem and one curious phenomenon:
Problem: If a form is returned and displayed in the container, the JS above tries to apply the same behavior to the form, so that whatever response is received after submitting is displayed in the container. This fails, as the submit event is never caught. Why? Note that all returned form elements have the class="ajax" attribute.
Phenomenon: Notice the console.debug() statement in ajaxifyForms(). I expect it to output to the console once after page load and then every time the container is updated with a form. The truth is that the number of outputs to the console seems to double for each time you click a link pointing to a form. Why?
I found another way to achieve what I wanted. In fact, the code for doing so is smaller and is less error prone. Instead of trying to make sure each link and form element on the page is observed at any given time, I utilize event bubbling and listen only to the document itself. Examining each event that bubbles up to it, I can determine whether it is subject for an AJAX request or not. Here's the new JS:
document.observe('submit', function(event) {
if (event.target.hasClassName('ajax')) {
event.stop();
event.target.request({
onSuccess: function(transport) {
$('ajax-container').update(transport.responseText);
}
});
}
});
document.observe('click', function(event) {
if (event.target.hasClassName('ajax')) {
event.stop();
new Ajax.Updater($('ajax-container'), event.target.href, {
method: 'get'
});
}
});
Works like a charm :)

JQuery mobile collapsible with Ajax

trying to display the JQMobile collapsible containing an unsorted list. The collapsible is not shown when the list is appended using an ajax call. The collapsible is correctly shown when the list is added statically. Any advice?
thanks
<script>
$(document).ready(function() {
var updateSectionsPage = function() {
// 1. Get the page and list we need to work with
var $page = $('#homeList');
// 2. Build the URL we need using the data stored on the main view page
var strUrl = 'http://xyz';
// 3. Get the sections and append them to the list
$.ajax({
url: strUrl,
dataType: 'json',
cache: false,
success: function(data) {
$sections = $page.find('#sections');
// 3.1 Delete the existing content, if any
$sections.empty();
// 3.2 Create a new collapsible
$sections.html('<div id="collapsible" data-role="collapsible" data-collapsed="true" data-theme="a" data-content-theme="a"></div>');
// 3.3 Create the title of collapsible
$sections.html('<h3>ColdPlay</h3>');
// 3.4 Create the list and store it into a JQuery object
$sections.html('<ul id="list" data-role="listview" data-inset="false"></ul>');
$list = $page.find('#list');
// 3.5 Build HTML that contains the desired information
for (var j in data.list[0].list){
var strHtml = '<li><img src="' + data.list[0].list[j].img + '" /><h4>' + data.list[0].list[j].title + '</h4></li>';
// Make it into a jQuery object...
var item = $(strHtml);
// ...so we can append it to our list.
$list.append(item);
}
// Call the listview widget.
$list.listview();
},
error: function() {
alert("An error occurred. please, try it again!");
}
});
}(); // 4. Call the updateSectionsPage() function
})
</script>
I think you just need to turn your $list.listview(); call into $list.listview('refresh');.
Also, you may benefit from changing up the way you append you new list items. Check this post out. You do not want to nest an append call within a loop if you can avoid it. You will also benefit from not wrapping your strHtml with the jQuery $ selector as it may not be necessary.
That optimization link is courtesy of another SO post here.
Once you create the list,use the following code snippet-
$list.listview('refresh');
$page.trigger('create');
in place of $list.listview();
Also it is not considered a best practice to use $(document).ready() in jquery mobile.See the note below
Important: Use pageInit(), not $(document).ready()
The first thing you learn in jQuery is to call code inside the
$(document).ready() function so everything will execute as soon as the
DOM is loaded. However, in jQuery Mobile, Ajax is used to load the
contents of each page into the DOM as you navigate, and the DOM ready
handler only executes for the first page. To execute code whenever a
new page is loaded and created, you can bind to the pageinit event.
This event is explained in detail at the bottom of this page.
From http://jquerymobile.com/demos/1.0/docs/api/events.html
thanks guys, the issue in my code was on the missing collapsible() widget call. Once the html page is dynamically created, we need to render it with jqmobile widget calls: listview() and collapsible(). Here the working code.
function fillSectionsPage() {
// 1. Get the page we need to work with
var $page = $('#sectionList');
// 2. Build the URL we need using the data stored on the main view page
var strUrl = 'http://xyz';
// 3. Get the sections and append them to the list
$.ajax({
url: strUrl,
dataType: 'json',
cache: false,
success: function(data) {
$sections = $page.find('#sections');
// 3.1 Delete the existing content, if any
$sections.empty();
// 3.2 Append a new collapsible and store it into a JQuery object
$sections.append('<div id="collapsible" data-role="collapsible" data-collapsed="true" data-theme="c" data-content-theme="c"></div>');
$collapsible = $page.find('#collapsible');
// 3.3 Append the title of collapsible
$collapsible.append('<h3>' + data.list[0].title + '</h3>');
// 3.4 Append the list header and store it into a JQuery object
$collapsible.append('<ul id="list" data-role="listview" data-inset="false"></ul>');
$list = $page.find('#list');
// 3.5 Build the list items
var htlmList = [];
for (var j in data.list[0].list){
htlmList[j] = '<li><img src="' + data.list[0].list[j].img + '" /><h4>' + data.list[0].list[j].title + '</h4></li>';
}
// 3.6 Append the list items to the list header
$list.append(htlmList.join(''));
// 3.7 Render the listview and the collapsible
$list.listview();
$collapsible.collapsible();
},
error: function() {
alert("An error occurred, please, try it again!");
}
});
}
Hope to check that tutorial, Collapsible content and Ajax loading with jQuery Mobile and this for How do I toggle the jQuery Mobile Accordion with a button click?

jqGrid trigger "Loading..." overlay

Does anyone know how to trigger the stock jqGrid "Loading..." overlay that gets displayed when the grid is loading? I know that I can use a jquery plugin without much effort but I'd like to be able to keep the look-n-feel of my application consistent with that of what is already used in jqGrid.
The closes thing I've found is this:
jqGrid display default "loading" message when updating a table / on custom update
n8
If you are searching for something like DisplayLoadingMessage() function. It does not exist in jqGrid. You can only set the loadui option of jqGrid to enable (default), disable or block. I personally prefer block. (see http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options). But I think it is not what you wanted.
The only thing which you can do, if you like the "Loading..." message from jqGrid, is to make the same one. I'll explain here what jqGrid does to display this message: Two hidden divs will be created. If you have a grid with id=list, this divs will look like following:
<div style="display: none" id="lui_list"
class="ui-widget-overlay jqgrid-overlay"></div>
<div style="display: none" id="load_list"
class="loading ui-state-default ui-state-active">Loading...</div>
where the text "Loading..." or "Lädt..." (in German) comes from $.jgrid.defaults.loadtext. The ids of divs will be constructed from the "lui_" or "load_" prefix and grid id ("list"). Before sending ajax request jqGrid makes one or two of this divs visible. It calls jQuery.show() function for the second div (id="load_list") if loadui option is enable. If loadui option is block, however, then both divs (id="lui_list" and id="load_list") will be shown with respect of .show() function. After the end of ajax request .hide() jQuery function will be called for one or two divs. It's all.
You will find the definition of all css classes in ui.jqgrid.css or jquery-ui-1.8.custom.css.
Now you have enough information to reproduce jqGrid "Loading..." message, but if I were you I would think one more time whether you really want to do this or whether the jQuery blockUI plugin is better for your goals.
I use
$('.loading').show();
$('.loading').hide();
It works fine without creating any new divs
Simple, to show it:
$("#myGrid").closest(".ui-jqgrid").find('.loading').show();
Then to hide it again
$("#myGrid").closest(".ui-jqgrid").find('.loading').hide();
I just placed below line in onSelectRow event of JQ grid it worked.
$('.loading').show();
The style to override is [.ui-jqgrid .loading].
You can call $("#load_").show() and .hide() where is the id of your grid.
its is worling with $('div.loading').show();
This is also useful even other components
$('#editDiv').dialog({
modal : true,
width : 'auto',
height : 'auto',
buttons : {
Ok : function() {
//Call Action to read wo and
**$('div.loading').show();**
var status = call(...)
if(status){
$.ajax({
type : "POST",
url : "./test",
data : {
...
},
async : false,
success : function(data) {
retVal = true;
},
error : function(xhr, status) {
retVal = false;
}
});
}
if (retVal == true) {
retVal = true;
$(this).dialog('close');
}
**$('div.loading').hide();**
},
Cancel : function() {
retVal = false;
$(this).dialog('close');
}
}
});
As mentioned by #Oleg the jQuery Block UI have lots of good features during developing an ajax base applications. With it you can block whole UI or a specific element called element Block
For the jqGrid you can put your grid in a div (sampleGrid) and then block the grid as:
$.extend($.jgrid.defaults, {
ajaxGridOptions : {
beforeSend: function(xhr) {
$("#sampleGrid").block();
},
complete: function(xhr) {
$("#sampleGrid").unblock();
},
error: function(jqXHR, textStatus, errorThrown) {
$("#sampleGrid").unblock();
}
}
});
If you want to not block and not make use of the builtin ajax call to get the data
datatype="local"
you can extend the jqgrid functions like so:
$.jgrid.extend({
// Loading function
loading: function (show) {
if (show === undefined) {
show = true;
}
// All elements of the jQuery object
this.each(function () {
if (!this.grid) return;
// Find the main parent container at level 4
// and display the loading element
$(this).parents().eq(3).find(".loading").toggle(show);
});
return show;
}
});
and then simple call
$("#myGrid").loading();
or
$("#myGrid").loading(true);
to show loading on all your grids (of course changing the grid id per grid) or
$("#myGrid").loading(false);
to hide the loading element, targeting specific grid in case you have multiple grids on the same page
In my issues I used
$('.jsgrid-load-panel').hide()
Then
$('.jsgrid-load-panel').show()

Resources