Disable Jquery UI widget completely - javascript

How do I do such a thing? I need to disable the widget completely, meaning that all of the instances should be disabled and NO MORE instances can be created after the disabling. I tried searching, but nothing comes up.
Will appreciate any help.
EDIT:
A more lively example.
Say, I have three instances of my widget placed on three elements. Then I want to turn my widget off. I invoke a static method turnOff, which leads to
a) all working instances to be disabled
b) prohibit any other instances of that widget to be created if they are later called via ajax i.e.
Then I want it to work again, so i invoke a turnOn().
My widget is a hint pugin, so if the user switches hints off, they should be switched off everywhere, and there are places in the app where hinted parts of the page are still being loaded asynchronosly.
That's pretty much what I need to do.

The answer depends on if your widget is obvious when not active -- for example if it's a 'hint' type tooltip that shows on hover then all you need to do is not show the tool tip when it's inactive. However if it also adds formatting to show where the hints are you need to undo that as well.
Let's take the simple case first -- suppose we have a simple widget filler that just adds a class (filled) when the mouse is over the element. Then just have a variable in global scope:
enableFiller = true;
and then check that in your widget:
$.widget( "spacedog.filler", {
_create: function() {
var progress = this.options.value + "%";
this.element.hover(
function() {
if (enableFiller) {
$(this).addClass("filled");
}
},
function() {
$(this).removeClass("filled");
}
);
}
});
Fiddle.
Note that I don't check the flag in the removeClass because the widget might be disabled while the mouse is over a widget and you probably don't want the hover color to accidentally 'stick on'.
From there it's pretty easy to extend to the case where your widget alters the element even when off. I'll do the logic as pseudo-code:
When creating the widget add a base-widget class to the element (even if inactive)
If the widget is on add the active-widget class and do whatever is required
To turn off remove the active-widget class from all base widgets: $(".base-widget").removeClass('active-widget'). You might also need to do some other changes to all the elements -- but they're easy to find.
To turn on add the active-widget class back and adjust everything else
You can embed the turnOn/turnOff functionality into the widget, as long as it uses the global variable as the flag. See this answer for other options for storing the global: How to declare a global variable in JavaScript?
If you get stuck you could post (a simplified version) of your widget code and what you've tried so far and someone can probably help further.

Related

Using addEventListener on multiple elements, avoid TypeError when particular element not found

I'm using two simple addEventListener mouseenter and mouseleave functions respectively to play and stop animations (Bodymovin/SVG animations, though I suspect that fact is irrelevant).
So, the following works fine:
document.getElementById('animationDiv').addEventListener('mouseenter', function(){
animation.play();
})
(The HTML couldn't be simpler: The relevant part is just an empty div placeholder filled by script - i.e., <div id="animationDiv"></div>.
I can place that in the same file as the one that operationalizes the animation code, or I can place it in a separate "trigger" file, with both files (and other others necessary to processing) loaded in the site footer.
The problem arises when I need to be able to set triggers for any of multiple similar animations that may or may not appear on a given page.
If only one of two animatable elements are present on a page, then one of two sets of triggers will throw an error. If the first of two such triggers is not present, then the second one will not be processed, meaning that the animation will fail. Or at least that's what it looks like to me is happening.
So, just to be clear, if I add the following two triggers for the same page, and the first of the following two elements is present, then the animation will play on mouseenter. If only the second is present, its animation won't be triggered, apparently because of the error thrown on the first.
document.getElementById('firstAnimationDiv').addEventListener('mouseenter', function(){
firstAnimation.play();
})
document.getElementById('secondAnimationDiv').addEventListener('mouseenter', function(){
secondAnimation.play();
})
At present I can work around the problem by creating multiple trigger files, one for each animation, and setting them to load only when I know that the animatable element will be present, but this approach would get increasingly inefficient when I am using multiple animations per page, on pages whose content may be altered.
I've looked at try/catch approaches and also at event delegation approaches, but so far they seem a bit complicated for handling this simple problem, if appropriate at all.
Is there an efficient and flexible standard method for preventing or properly handling an error for an element not found, in such a way that subsequent functions can still be processed? Or am I missing something else or somehow misreading the error and the function failure I've been encountering?
WHY I PICKED THE ANSWER THAT I DID (PLUS WORKING CODE)
I was easily able to make the simple, directly responsive answer by Baoo work.
I was unable to make the answers below by Patrick Roberts and Crazy Train work, though no doubt my undeveloped js skills are entirely at fault. When I have the time, or when the issue next comes up for me in a more complex implementation (possibly soon!), I'll take another look at their solutions, and see if I can either make them work or if I can formulate a better question with fully fledged coding examples to be worked through.
Finally, just to make things clear for people who might be looking for an answer on Bodymovin animations, and whose js is even weaker than mine, the following is working code, all added to the same single file in which a larger set of Bodymovin animations are constructed, relieving me of any need to create separate trigger files, and preventing TypeErrors and impaired functionality.
//There are three "lets_talk" animations that can play - "home," "snug," and "fixed"
//and three types of buttons needing enter and leave play and stop triggers
let home = document.getElementById('myBtn_bm_home');
if (home) home.addEventListener('mouseenter', function() {
lets_talk_home.play();
});
if (home) home.addEventListener('mouseleave', function() {
lets_talk_home.stop();
});
let snug = document.getElementById('myBtn_bm_snug');
if (snug) snug.addEventListener('mouseenter', function() {
lets_talk_snug.play();
});
if (snug) snug.addEventListener('mouseleave', function() {
lets_talk_snug.stop();
});
let fixed = document.getElementById('myBtn_bm_fixed');
if (fixed) fixed.addEventListener('mouseenter', function() {
lets_talk_fixed.play();
});
if (fixed) fixed.addEventListener('mouseleave', function() {
lets_talk_fixed.stop();
});
At typical piece of underlying HTML (it's generated by a PHP function taking into account other conditions, so not identical for each button), looks like this at the moment - although I'll be paring away the data-attribute and class, since I'm not currently using either. I provide it on the off-chance that someone sees something significant or useful there.
<div id="letsTalk" class="lets-talk">
<a id="myBtn" href="#"><!-- a default-prevented link to a pop-up modal -->
<div class="bm-button" id="myBtn_bm_snug" data-animation="snug"></div><!-- "snug" (vs "fixed" or "home" is in both instances added by PHP -->
</a>
</div>
Obviously, a more parsimonious and flexible answer could be - and probably should be - written. On that note, correctly combining both the play and stop listeners within a single conditional would be an obvious first step, but I'm too much of a js plodder even to get that right on a first or second try. Maybe later/next time!
Thanks again to everyone who provided an answer. I won't ask you to try to squeeze the working solution into your suggested framework - but I won't ask you not to either...
Just write your code so that it won't throw an error if the element isn't present, by simply checking if the element exists.
let first = document.getElementById('firstAnimationDiv');
if (first) first.addEventListener('mouseenter', function() {firstAnimation.play();});
You could approach this slightly differently using delegated event handling. mouseover, unlike mouseenter, bubbles to its ancestor elements, so you could add a single event listener to an ancestor element where every #animationDiv is contained, and switch on event.target.id to call the correct play() method:
document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
switch (event.target.id) {
case 'firstAnimationDiv':
return firstAnimation.play();
case 'secondAnimationDiv':
return secondAnimation.play();
// and so on
}
});
You could also avoid using id and use a more semantically correct attribute like data-animation as a compromise between this approach and #CrazyTrain's:
document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
// assuming <div data-animation="...">
// instead of <div id="...">
switch (event.target.dataset.animation) {
case 'first':
return firstAnimation.play();
case 'second':
return secondAnimation.play();
// and so on
}
});
First, refactor your HTML to add a common class to all of the placeholder divs instead of using unique IDs. Also add a data-animation attribute to reference the desired animation.
<div class="animation" data-animation="first"></div>
<div class="animation" data-animation="second"></div>
The data- attribute should have a value that targets the appropriate animation.
(As #PatrickRobers noted, the DOM selection can be based on the data-animation attribute, so the class isn't really needed.)
Since your animations are held as global variables, you can use the value of data-animation to look up that variable. However, it would be better if they weren't global, but were rather in a common object.
const animations = {
first: null, // your first animation
second: null, // your second animation
};
Then select the placeholder elements by class, and use the data attribute to see if the animation exists, and if so, play it.
const divs = document.querySelectorAll("div.animation");
divs.forEach(div => {
const anim = animations[div.dataset.animation];
if (anim) {
anim.play(); // Found the animation for this div, so play it
}
});
This way you're guaranteed only to work with placeholder divs that exist and animations that exist.
(And as noted above, selection using the data attribute can be done const divs = document.querySelectorAll("div[data-animation]"); so the class becomes unnecessary.)

How to re-enable jQuery tooltip after disabled = true?

Trying to temporarily disable the jQuery tooltip
$(document).tooltip( "option", "disabled", true );
When I try to re-enable them again, all of the title attributes are gone. I was trying to re-enable them using:
$(document).tooltip( "option", "disabled", false);
The title attributes is actually being removed completely when i first set it to disabled to true.
Also tried:
$(document).tooltip("disable");
$(document).tooltip("enable");
It is doing the same thing...
Update
Found a solution to my own question with the help of Jasen and zgr024. See below.
This appears to be a bug with the jquery version so you need a work-around to insert the title attribute after disabling tooltips.
Use a class name on the tooltip element you need re-enabled or use the [title] attribute selector.
<input type="text" class="tooltip-hack" title="tooltip text" />
$("#disable").on("click", function (e) {
var tooltips = $("[title]"); // or $(".tooltip-hack")
$(document).tooltip("disable");
tooltips.attr("title", "");
});
Use the least destructive of the two depending on your html structure.
Also, you note that all title attributes are removed so be more selective with your tooltip.
// instead of
$(document).tooltip();
// use a more restrictive selector
$(".tooltip-hack").tooltip();
Working example: jsFiddle
So with the help of others above, I finally found a better solution. That require no brutal fix to re-adding title to every single elements. Don't get me wrong, that fix will work, but performance is important (especially I still need to support IE8 ugh).
Basically I add a custom variable to the tooltip object, this can also be a global variable. Since everything is an Object in js, you can just add anything you want to it.
$(document).tooltip.temporarilyOff
Then when I initialize the jQuery tooltip, I just need to add a check in the open:
var settings = {};
settings.tooltipClass = "tooltip";
settings.open = function (event, ui) {
if ($(document).tooltip.temporarilyOff) {
ui.tooltip.stop().remove();
}
};
$(document).tooltip(settings);
Then when I need to temporarily disable the jQuery tooltip, I just need to toggle the flag anywhere I want. Like so:
$(document).tooltip.temporarilyOff = true;
Anything after this point, the tooltip won't be triggered, and all the elements will keep their title attributes. When I am done with what I am doing, I just need to set the flag back to false and tooltip will work exactly like before.
I can probably make this into jQuery plugin for easier calls and also hide the slightly ugly variable name... but anyway that's the idea. I think this is a much better fix as it won't force jQuery to remove the title attribute for nothing, then adding it back afterward doing twice the useless work.
Here is the updated example forked from #Jasen's original jsFiddle:
Codenamezero is a nice example of extending an object in jQuery/js but you can do this out-of-the-box:
$(document).tooltip("disable").tooltip("hide");
$(document).tooltip("enable").tooltip("show");
I have validation where this method can be useful to override defaults

What is the best way to implement multiway toggle using javascript/jQuery?

I have a div that can display 3 images (in the background) each indicating the 'state' of some variable: i.e., partial, full and none. For each of these states I have images: partial.gif, full.gif and none.gif (i.e., these are background images of that div)
Need: Circular queue like toggling effect for changing the images in this order partial -> full -> none -> partial
So if the current image is 'partial.gif' and the user clicks the div the background image changes to the next one in the sequence i.e., full.gif (and if it is currently full.gif it changes to none.gif and that to partial.gif and so on).
Naive solution: have a bunch of if/else's or switch-case and check the current one (image) and then decide based on array look up which is the next one. Is this the best way of doing it? Can I leverage jQuery's toggle function somehow?
(PS: It need not be restricted to images, but could also be for different background color sequences etc., I'd like to know what it is a good 'generic' way of doing it i.e., The example may be specific for background images but if I changed part of that code for background-color or font it should still work. I don't want it to be purely generic, but just enough so it is easy to modify for other attributes. If not, that's fine too. Just a thought :)
http://api.jquery.com/toggle-event/
To be precise http://api.jquery.com/toggle-event/#example-0
does exactly what you wanted...
$("#div1").toggle(
function() {
$(this).css("background-image","url(full.png)")
},
function() {
$(this).css("background-image","url()")
},
function() {
$(this).css("background-image","url(partial.png)")
}
});
UPDATE fn.toggle was removed from jQuery
Here are relevant posts
Where has fn.toggle( handler(eventObject), handler(eventObject)...) gone?
Toggle stopped working after jquery update
As long as it's a CSS-based solution (where you can just switch classes), you could do something like this (untested code):
$('#element').click(function() {
// get current css class value.
var class = $(this).attr('class');
// determine/increment number.
var nextNumber = parseInt(class.charAt(class.length - 1)) + 1;
// if too high, reset to first one.
if (nextNumber > 3) {
nextNumber = 1;
}
// remove old class and add new class.
$(this).removeClass(class).addClass('my_class' + nextNumber);
});
Assumption being made here that you only have one CSS class applied to the element at a time. But if that's not the case, I'm sure you can find a workaround/tweak for this.
And this is just generic enough where you can swap out your CSS class definitions without impacting the script functionality.

JQuery: Why is hoverIntent not a function here?

I'm modifying some code from a question asked a few months ago, and I keep getting stymied. The bottom line is, I hover over Anchors, which is meant to fade in corresponding divs and also apply a "highlight" class to the Anchor. I can use base JQuery and get "OK" results, but mouse events are making the user experience less than smooth.
I load JQuery 1.3 via Google APIs.
And it seems to work. I can use the built in hover() or mouseover(), and fadeIn() is intact... no JavaScript errors, etc. So, JQuery itself is clearly "loaded". But I was facing a problem that it seemed everyone was recommending hoverIntent to solve.
After loading JQuery, I load the hoverIntent JavaScript. I've triple-checked the path, and even dummy-proofed the path. I just don't see any reasonable way it can be a question of path.
Once the external javascripts are (allegedly) loaded in, I continue with my page's script:
var $old=null;
$(function () {
$("#rollover a").hoverIntent(doSwitch,doNothing)
});
function doNothing() {};
function doSwitch() {
var $this = $(this);
var $index = $this.attr("id").replace(/switch/, ""); //extract the index number of the ID by subtracting the text "switch" from its name
if($old!=null) $old.removeClass("highlight"); //remove the highlight class from the old (previous) switch before adding that class to the next
$this.addClass("highlight"); //adds the class "highlight" to the current switch div
$("#panels div").hide(); //hide the divs inside panels
$("#panel" + $index).fadeIn(300); //show the panel div "panel + number" -- so if switch2 is used, panel2 will be shown
$old = $this; //declare that the current switch div is now "old". When the function is called again, the old highlight can be removed.
};
I get the error:
Error: $("#rollover a").hoverIntent is not a function
If I change to a known-working function like hover (just change ".hoverIntent" to ".hover") it "works" again. I'm sure this is a basic question but I'm a total hack when it comes to this (as you can see by my code).
Now, for all appearances, it SEEMS like either the path is wrong (I've zillion-checked and even put it on an external site with an HTTP link that I double-checked; it's not wrong), or the .js doesn't declare the function. If it's the latter, I must be missing a few lines of code to make the function available, but I couldn't find anything on the author's site. In his source code he uses a $(document).ready, which I also tried to emulate, but maybe I did that wrong, too.
Again, the weird bit is that .hover works fine, .hoverIntent doesn't. I can't figure out why it's not considered a function.
Trying to avoid missing anything... let's see... there are no other JavaScripts being called. This post contains all the Javascript the page uses... I tried doing it as per the author's var config example (hoverIntent is still not a function).
I get the itching feeling I'm just missing one line to declare the function, but I can't for the life of me figure out what it is, or why it's not already declared in the external .js file. Thanks for any insight!
Greg
Update:
The weirdest thing, since I'm on it... and actually, if this gets solved, I might not need hoverIntent solved:
I add an alert to the "doNothing" function and revert back to plain old .hover, just to see what's going on. For 2 of my 5 Anchors, as soon as I hover, doNothing() gets called and I see the alert. For the other 3, doNothing() correctly does NOT get called until mouseout. As you can see, the same function should apply for any Anchor inside of "rollover" div. I don't know why it's being particular.
But:
If I change fadeIn to another effect like slideDown, doNothing() correctly does NOT get called until mouseout.
when using fadeIn, doNothing() doesn't get called in Opera, but seems to get called in pretty much all other browsers.
Is it possible that fadeIn itself is buggy, or is it just that I need to pass it an appropriate callback? I don't know what that callback would be, if so.
Cheers for your long attention spans...
Greg
Hope I didn't waste too many people's time...
As it turns out, the second problem was 2 feet from the screen, too. I suspected it would have to do with the HTML/CSS because it was odd that only 2 out of 5 elements exhibited strange behaviour.
So, checked my code, dug out our friend FireBug, and discovered that I was hovering over another div that overlapped my rollover div. Reason being? In the CSS I had called it .panels instead of .panel, and the classname is .panel. So, it used defaults for the div... ie. 100% width...
Question is answered... "Be more careful"
Matt and Mak forced me to umpteen-check my code and sure enough I reloaded JQuery after loading another plugin and inserting my own code. Since hoverIntent modifies JQuery's hover() in order to work, re-loading JQuery mucked it up.
That solved, logic dictated I re-examine my HTML/CSS in order to investigate my fadeIn() weirdness... and sure enough, I had a typo in a class which caused some havoc.
Dumb dumb dumb... But now I can sleep.

jquery ui.slider: add click event to display/hide handle

I am completely new to javascript and jquery. My programming knowledge is... nonexistent, I just started some days ago with some simple tasks like replacing a CSS class or toggling a div. So I want to apologize if I'm treading on someones toes by asking newbie-questions in here. But I hope that someone can help me and solve my problem.
I need to implement some sort of visual analog scale for a survey; ui.slider is perfect for that one. But I need the handle to be hidden by default. When the user clicks on the slider, the handle shall appear in the proper position. That should be fairly simple - at least I hope so - by just hiding the handle with CSS and changing it by a click event on the slider.
I use the following piece of code to wrap a normal div (a div is needed in my understanding to apply the jquery slider.js) to my input elements (they should be - at least visually - replaced by the slider) and pass the value of the slider to the input elements (needed for passing the values to a form). that works properly. (I do that instead of just putting a div in my DOM by default because I cannot influence some PHP scripts that will generate form elements of the survey and so on)
$(function () {
$.each($('.slider'),
function () {
obj = $(this);
obj.wrap('<div></div>');
obj.parent().slider({
change: function (event, ui) {
$('input', this).val(ui.value);
}
});
});
});
Hiding the slider-handle can be done by CSS as described above by changing style properties of a.ui-slider-handle. but when I add a normal click event to the slider (.ui-slider) that changes CSS properties of the handle, nothing happens. As far as my basic knowledge goes it should have something to do with the click event not working on generated DOM elements. Am I right with that one? And if yes: how can I solve this problem? Could someone provide me a piece of code for my function and explain it so I might comprehend what's exactly going on?
I read a tutorial about events on learningjquery.com but I have not made enough progresses the last few days since I started working with JS/jquery to comprehend the steps and translate it into my example/problem. And I am running out of time (I need this for a survey I have to make asap, that's why I hope someone could give me a hint so I can solve this little issue somehow).
Any reason you can't just include the show on the change event rather than a click? It's a bit cleaner code-wise rather than including a whole new event.
$(function() {
$('.slider').wrap('<div></div>').parent().slider({
change: function(event, ui) {
$('input', this).val(ui.value);
$('.ui-slider-handle').show();
}
});
});
Also, there was a bit of redundancy in the code - most jQuery functions return the object itself, so you can chain them. And you don't need that each function, since most jQuery functions also, when applied to a collection, run on all of them :)

Categories