jQuery UI tooltip on pseudo disabled elements - jquery-ui

I would like to show a tooltip on a text input that has a ui-state-disabled class.
I took a peek to the tooltip source code and I couldn't find something that checks against that particular class. So I don't know why it won't show.
As far as I can tell, the elements aren't disabled per se, they just have a class applied to them.
So, how can I show a tooltip on elements that have that class? I don't want to use a wrapper or anything like that. Maybe extending through widget factory...
Here's a sample code
HTML
<input name="#1" class="text" data-tooltip="message A">
<input name="#2" class="text" data-tooltip="message B">
<br>
<button id="disable">disable input #2</button>
<button id="enable">enable input #2</button>
JS
$(".text").each(function()
{
$(this).tooltip({
content: $(this).data("tooltip"),
items: ".text"
});
});
$("#disable").click(function()
{
$("input[name='#2']").addClass("ui-state-disabled");
});
$("#enable").click(function()
{
$("input[name='#2']").removeClass("ui-state-disabled");
});
FIDDLE: https://jsfiddle.net/hn1o4qs2/

See the doc (http://api.jqueryui.com/tooltip/):
In general, disabled elements do not trigger any DOM events.
Therefore, it is not possible to properly control tooltips for
disabled elements, since we need to listen to events to determine when
to show and hide the tooltip. As a result, jQuery UI does not
guarantee any level of support for tooltips attached to disabled
elements. Unfortunately, this means that if you require tooltips on
disabled elements, you may end up with a mixture of native tooltips
and jQuery UI tooltips.
Solution with wrapper
Your HTML:
<span class="input-container" data-tooltip="message A">
<input name="#1" class="text">
</span>
<span class="input-container" data-tooltip="message B">
<input name="#2" class="text">
</span>
<br>
<button id="disable">
disable input #2
</button>
<button id="enable">
enable input #2
</button>
Your Javascript
$(".input-container").each(function()
{
$(this).tooltip({
content: $(this).data("tooltip"),
items: ".input-container"
});
});
// ... The rest is the same
Solution with fake disabled-property
Here you can use a readonly attribute and a custom class for disabled input.
Playground: https://jsfiddle.net/5gkx8qec/

As I've stated in my question, I needed to get this working without adding a container or anything like that. And I was willing to extend the widget somehow...
So I read the source code more carefully and searched throught the whole repository for ui-state-disabled, and found that in widget.js there is an _on() method that at some point performs a check against that class and a flag called suppressDisabledCheck
A comment in code says
// Allow widgets to customize the disabled handling
// - disabled as an array instead of boolean
// - disabled class as method for disabling individual parts
This was very important, it gave me the clue that this check could be overriden. So a quick search in google and the widget factory had the answer:
Automatically handles disabled widgets: If the widget is disabled or
the event occurs on an element with the ui-state-disabled class, the
event handler is not invoked. Can be overridden with the
suppressDisabledCheck parameter.
So basically I did this:
$.widget("ui.tooltip", $.ui.tooltip,
{
options: {
allowOnDisabled: false
},
_on: function()
{
var instance = this;
this._super(instance.options.allowOnDisabled, {
mouseover: "open",
focusin: "open",
mouseleave: "close",
focusout: "close"
});
}
});
And then used it like this:
$(".text").each(function()
{
$(this).tooltip({
allowOnDisabled: true,
content: $(this).data("tooltip"),
items: ".text"
});
});
EDIT 2022-09-15
I was having some trouble with this implementation, so I've changed it a little bit
$.widget("ui.tooltip", $.ui.tooltip,
{
options: {
allowOnDisabled: false
},
_create: function()
{
this._super();
var instance = this;
this._on(instance.options.allowOnDisabled, {
mouseover: "open",
focusin: "open",
mouseleave: "close",
focusout: "close"
});
}
});

Related

Knockout Binding Not Working with jQueryUI Dialogue

My viewModel has an array called 'Items'. I want to display the contents of 'Items' using a foreach binding. Everything works fine when I use regular HTML. But does not work with a dialogue box which I created using jQueryUI.
HTML:
<div id="skus0">
<div id="skus1">
<ul data-bind="foreach: Items">
<li data-bind="text:Name"></li>
</ul>
</div>
<input type="button" id="openQryItems" class="btn btn-info" value="Open" data-bind="click:openQueryItems" />
</div>
JavaScript:
// my view model
var viewModel = {
Items: [{Name:'Soap'},{Name:'Toothpaste'}]
};
// JS to configure dialogue
$("#skus1").dialog({
autoOpen: false,
width: 500,
modal: true,
buttons: {
"OK": function () {
$(this).dialog("close");
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
// for mapping my model using ko.mapping plugin
var zub = zub || {};
zub.initModel = function (model) {
zub.cycleCountModel = ko.mapping.fromJS(model);
zub.cycleCountModel.openQueryItems = function () {
$("#skus1").dialog("open");
}
ko.applyBindings(zub.cycleCountModel, $("#skus0")[0]);
}
zub.initModel(viewModel);
I have created a fiddle here my fiddle
$.fn.dialog removes the element from its place in the DOM and places it in a new container; this is how it can create a floating window. The problem with this happening is that it breaks data binding, since the dialog DOM is no-longer nested within the top-level data-bound DOM.
Moving the dialog initialization to after ko.applyBindings will enable dialog to yank stuff out of the DOM after the list is populated. Of course, this means that after that point, future changes will still not be reflected, which may be important if you're wanting the opened dialog to change automatically.
If you are wanting the dialog contents to be fully dynamic, you could create a binding handler; we did this in our project. Here's a rough outline of how we did this:
ko.bindingHandlers.dialog = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingCtx) {
var bindingValues = valueAccessor();
var hasAppliedBindings = false;
var elem = $(element);
var options = {
id: ko.utils.unwrapObservable(bindingValues.id),
title: ko.utils.unwrapObservable(bindingValues.title),
// etc...
onOpen: function () {
if (!hasAppliedBindings) {
hasAppliedBindings = true;
var childCtx = bindingCtx.createChildContext(viewModel);
ko.applyBindingsToDescendants(childCtx, element);
}
}
};
elem.dialog(options);
}
return { controlsDescendantBindings: true };
}
...which we used like this:
<div data-bind="dialog: { title: 'some title', id: 'foo', ... }">
<!-- dialog contents -->
</div>
What return { controlsDescendantBindings: true } does is makes sure that outer bindings do not affect anything using the dialog binding handler. Then we create our own Knockout binding "island" after it is pulled out of the DOM, based on the original view model.
Although in our project we also used hybrid jQuery+Knockout, I would highly recommend you avoid this whenever possible. There were so many hacks we had to employ to sustain this type of application. The very best thing you should do is prefer Knockout binding handlers (and I think it has a "component" concept now which I haven't played with) over DOM manipulations to avoid buggy UI management.

Specify jQuery UI Tooltip CSS styles

I am using jquery ui 1.9 in an ajax based website.
I have the following code:
This is a <span title="Some warning" class="warning">warning</span> message<br />
This is a <span title="Some info" class="info">info</span> message
Using jquery ui tooltip would work, even for dynamic content:
$(function() {
$( document ).tooltip();
});
But I want different tooltip styles for each of this message-types. For example red color for warning and blue for info and it should work for dynamic content too.
Any ideas?
You need to use the toolTipClass property to specify the css class
$(document).ready(function() {
$( ".warning" ).tooltip({
tooltipClass: "warning-tooltip"
});
$( ".info" ).tooltip({
tooltipClass: "info-tooltip"
});
});
First, here is the code that works:
$(function() {
$('#warning-binder-element').tooltip({
items: '.warning',
tooltipClass: 'warning-tooltip',
content: function () {
return $(this).prev('.warning-toast').html();
},
position: {
my: "right bottom",
at: "right top-10"
}
});
$('#info-binder-element').tooltip({
items: '.info',
tooltipClass: 'info-tooltip',
content: function () {
return $(this).closest('.doo-hicky').next('.info-toast').html();
},
position: {
my: "left bottom",
at: "left+10 top-10"
}
});
});
A few notes on the above:
The selector for .tooltip() is not the item you want to have a tooltip pop up on, it is an element on the page that the tooltip object and its associated events are bound to.
If you attempt to bind two tooltips to the same object, only the last one will persist so binding both to $(document) will not work (which is why I have bound the two different tooltip objects to two different elements on the page).
you can bind the tooltip object to the item that will get the tooltip, but if you use a class selector, this may lead to ill effects.

pass variable to jquery dialog

I am wanting to pass a variable to a jquery dialog window. I am using classic ASP and have onyl really touched on the world of jquery. I have spent a few hours researching and tring to use the .data method but are having trouble. Your time and assitance would be very much apriciated!
Here is my stripped down page:
$(function(){
// Dialog
$('#dialog').dialog({
autoOpen: false,
show: "slide",
hide: "fade",
width: 452,
modal: true,
buttons: {
"Ok": function() {
PostItNote.submit();
$(this).dialog("close");
},
"Cancel": function() {
$(this).dialog("close");
}
}
});
// Dialog Link
$('#dialog_link').click(function(){
$('#dialog').dialog('open');
return false;
});
});
and this is how I call the window:
<a href='#' id='dialog_link' CLASS='OrangeButton'> Show Window </a>
and the contents of my window:
<div ID="dialog" TITLE="Periodic Report">
stuff here
</div>
Why can I not do this:
<a href='#' id='dialog_link(someValue)' CLASS='OrangeButton'> Show Window </a>
Thank you in advance
How it is used in the ASP loop is:
do until Products.EOF
--other code here---
<a href='#' id='dialog_link(Products(0))' CLASS='OrangeButton'>
--other code here---
products.moveNext
loop
The dialog is just a div on your page, so it can't really be "passed" a value. It can be manipulated by any JavaScript variables in scope though. You could change your click handler to use a variable to manipulate the dialog:
var myVariable = "Some new text for the dialog";
$('#dialog_link').click(function(){
//New code to "pass" a value to the dialog
$('#dialog').text(myVariable);
$('#dialog').dialog('open');
return false;
});
Or you could use the open member of the dialog:
...
width: 452,
open: function() { $('#dialog').text(myVariable); },
modal: true,
...
To make changes to the dialog div whenever it is opened.
The code id='dialog_link(someValue)' will not do anything, as the id attribute cannot make function calls, only event handlers like onchange can do that. Even if it could, dialog_link is not a function that can be passed a value. It is just the id of another element.
I'm sure you're probably already aware, but the jQuery documentation is very useful- here are the docs for dialog.
Edit
In response to your comment: I would drop the $('#dialog_link').click(); statement and change the link code to the following:
<a href='#' class='OrangeButton' onclick='openDialog(someValue);'>Show Window</a>
And then add the JavaScript function to be called on the click event:
function openDialog(value) {
$('#dialog').text(value);
$('#dialog').dialog('open');
}
Edit 2
Inside of the ASP loop something like this should do the trick:
Response.Write("<a href='#' onclick='openDialog(" & Products(0) & ");'>Show</a>")
This will create an <a></a> element on the page with an onclick handler that passes the desired value to openDialog, making it accessible by the dialog.
I changed the code for the link slightly so that it all fit on one line, but you could always add back the class='OrangeButton' and all that if you'd like.

jQuery UI Sortable child triggers

I have a list like so:
<ol id="page_items">
<li>
<label for="page_body_1">Content Area 1</label>
<textarea name="page_body_1" class="page_content_area" rows="10"></textarea>
</li>
<li>
<label for="page_body_2">Content Area 2</label>
<textarea name="page_body_2" class="page_content_area" rows="10"></textarea>
</li>
</ol>
When the page loads, #page_items turns into a tinyMCE editor. What I want is for the element that defines whether or not the li elements are being sorted to be the <label> but no other child elements of li. So the only element that starts the sort is the label.
Here's my jQuery:
$(document).ready(function(){
$("#page_items").sortable({
activate: function(event, ui) {
var EditorID = ui.item.find('textarea').attr('id');
if ( EditorID ){
tinyMCE.execCommand("mceRemoveControl", false, EditorID);
$('#'+EditorID).hide();
}
},
stop: function(event, ui) {
var EditorID = ui.item.find('textarea').attr('id');
if ( EditorID ){
$('#'+EditorID).show();
tinyMCE.execCommand("mceAddControl", false, EditorID);
delete EditorID;
}
}
});
});
In case anyone is wondering, I'm disabling the tinyMCE because in FireFox, moving an iFrame around the DOM clears it's contents and doesn't allow focus back on it.
Is there a way to cancel the sortable if the element clicked isn't the label?
If anyone has any code clean-up suggestions they are also welcome!
Thanks.
This turned out to be a sortable option that I didn't see before (I looked... oh I looked). The handle option is what I need. This initializes a sortable with the handle option specified.
Simply...
$(document).ready(function(){
$("#page_items").sortable({
handle: 'label'
});
});

JQuery UI Tabs: Apply opacity toggle to only specific inner element?

I am using Jquery UI tabs, and have it set to toggle the opacity with each slide change. I'm wondering if there's a way to apply the opacity toggle to only a single element within each tab, instead of the entire tab. My understanding of jQuery is pretty basic, so bear with me.
So, If I have something like this:
<div id="tabs">
<ul id="tabs-nav><li></li></ul>
<div id="tab-1">
<img />
<p />
</div>
<div id="tab-2">
<img />
<p />
</div>
...etc
</div>
How could I set it so that only the <img> has an effect applied, and the rest just switches normally?
Here are the basics of the call I have for UI tabs:
var $tabs = $('#slides').tabs({fx: { opacity: 'toggle' } });
$(".ui-tabs-panel").each(function(i){
//stuff to create previous/next links
});
$('.next-tab, .prev-tab').click(function() {
$tabs.tabs('select', $(this).attr("rel"));
return false;
});
UPDATE: So this is what I ended up with based on karim79's suggestions, and it seems to work. I added this after the original code I showed above (and removed {fx: { opacity: 'toggle' } } from my original code):
$( "#slides" ).bind( "tabsselect", function(event, ui) {
$(ui.panel).find("img").fadeOut().fadeIn();
});
I'm going to explain my logic, because like I said, my understanding is basic, so I'd love to know if my rationale is off!
I removed karim79's coniditional statement, because I want this to apply to ALL of the tabs. Am I correct in understanding that an if(ui.index == 2) would only apply to the third tab?
Then, I changed the .css("opacity", 0.6) to .fadeOut().fadeIn() because the .css opacity was only succeeding in making all of the slides semi-transparent, but not fading anything in or out.
Would this be an acceptable way of doing this or is it somehow hackish?
This should do it:
$( ".selector" ).bind( "tabsselect", function(event, ui) {
if(ui.index == 2) { // or whatever index you're interested in
$(ui.panel).find("img").css("opacity", 0.6);
}
});

Resources