How to destroy all tooltips Bootstrap 5 - bootstrap-5

Hi lets say we enable Tooltip everywhere take from this example https://getbootstrap.com/docs/5.0/components/tooltips/#example-enable-tooltips-everywhere
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
After certain operation in the page, I want to dispose all the tooltips.
Since tooltipList is an array, I loop all the tooltip and dispose each tooltip
tooltipList.forEach(function (tooltip, index) {
tooltip.dispose();
});
This is working fine for me, but question is it the right way to dispose tooltip in Bootstrap 5?
In Bootstrap 4 the code much cleaner
$('[data-toggle="tooltip"]').tooltip('dispose');
Thanks
Question

Related

How to sync model watched by ngRepeat when the repeated DOMs are modified externally?

I have two lists which are rendered by my directive. The requirement is that user can move an item from one list to another. I have a simplified implementation of this below:-
http://jsfiddle.net/yK7Lt/
The above shows a demo of how it should behave. Notice in this I manipulate the model and the DOM auto-syncs with it.
However, the problem is I am using jquery-ui-sortable plugin. So, the user can drag and drop the item from one list to another. Since jQuery is unaware of AngularJs so it modified the DOM. Now in my directive I have placed the code to sync the underlying model with the changed DOM.
The below jsfiddle code is a simplified version of my code.
http://jsfiddle.net/5Xuz2/1/
The relevant code snippet is:-
$('#btn').on('click', function () {
var li = $('#left li').first().detach();
$('#right').prepend(li);
console.log('moved top DOM to right list');
angular.element('#left').scope().$apply(function () {
// The moment this code runs, the DOM related to i is
// marked with $$NG_REMOVED, and is removed from page.
// Also somehow the DOM related to item D too is removed.
i = itemsl.shift(); // i is global variable.
});
angular.element('#right').scope().$apply(function () {
itemsr.unshift(i);
console.log('synced data with DOM');
});
});
The problem I am facing with my implementation is that the right list empties out as soon as I sync my left list model.
What is wrong with my implementation?
Is there a better approach?
the problem here is you are manipulating DOM with both Angular and jQuery... if you remove this piece of code
var li = $('#left li').first().detach();
$('#right').prepend(li);
it is working as expected
btw. I suggest trying angular-UI instead of jQueryUI
edit: OR you can try to refactor your code to something like this
var itemsl, itemsr, i, move;
function Model(name) {
this.name = name;
}
function Ctrl($scope) {
itemsl = $scope.itemsl = [new Model('A'), new Model('B'), new Model('C')];
itemsr = $scope.itemsr = [new Model('D')];
move = function() {
$scope.$apply(function() {
i = itemsl.slice(0,1);
itemsl.splice(0,1);
itemsr.unshift(i[0]);
i = null;
});
}
}
$(function () {
$('#btn').on('click', function () {
console.log('moved top DOM to right list');
move();
});
});

Drag and Drop with Angular JS and JQuery

Couple of days ago I found this interesting post at http://www.smartjava.org/content/drag-and-drop-angularjs-using-jquery-ui and applied it into my website. However when I progressively using it there is a bug I identified, basically you can not move an item directly from one div to another's bottom, it has to go through the parts above and progress to the bottom. Anyone can suggest where does it goes wrong? The example is at http://www.smartjava.org/examples/dnd/double.html
Troubling me for days already.....
I did this a bit differently. Instead of attaching a jquery ui element inside the directive's controller, I instead did it inside the directive's link function. I came up with my solution, based on a blog post by Ben Farrell.
Note, that this is a Rails app, and I am using the acts_as_list gem to calculate positioning.
app.directive('sortable', function() {
return {
restrict: 'A',
link: function(scope, elt, attrs) {
// the card that will be moved
scope.movedCard = {};
return elt.sortable({
connectWith: ".deck",
revert: true,
items: '.card',
stop: function(evt, ui) {
return scope.$apply(function() {
// the deck the card is being moved to
// deck-id is an element attribute I defined
scope.movedCard.toDeck = parseInt(ui.item[0].parentElement.attributes['deck-id'].value);
// the id of the card being moved
// the card id is an attribute I definied
scope.movedCard.id = parseInt(ui.item[0].attributes['card-id'].value);
// edge case that handles a card being added to the end of the list
if (ui.item[0].nextElementSibling !== null) {
scope.movedCard.pos = parseInt(ui.item[0].nextElementSibling.attributes['card-pos'].value - 1);
} else {
// the card is being added to the very end of the list
scope.movedCard.pos = parseInt(ui.item[0].previousElementSibling.attributes['card-pos'].value + 1);
}
// broadcast to child scopes the movedCard event
return scope.$broadcast('movedCardEvent', scope.movedCard);
});
}
});
}
};
});
Important points
I utilize card attributes to store a card's id, deck, and position, in order to allow the jQuery sortable widget to grab onto.
After the stop event is called, I immediately execute a scope.$apply function to get back into, what Misko Hevery call,s the angular execution context.
I have a working example of this in action, up in a GitHub Repo of mine.

jQuery UI Accordion - does refresh method overwrites initialisation settings?

Currently I am working on a project for which I use the jQuery UI Accordion.
Therefore I initialise the accordion on an element by doing
<div id="accordion"></div>
$('#accordion').accordion({
collapsible: true,
active: false,
heightStyle: "content"
});
After init the accordion I append some data coming from an AJAX request. (depends on user interaction)
In a simplified jsfiddle - which does exact the same thing as the ajax call - you can see how this looks like.
So far it seems to be working quite well but there is one problem I face.
In my initialisation I say that I want all panels to be closed but after calling refresh on the accordion everything of those settings seems to be gone and one panel opens.
Note that I implemented jQuery UI v1.10.2 in my fiddle. Update notes say
The refresh method will now recognize panels that have been added or removed. This brings accordion in line with tabs and other widgets that parse the markup to find changes.
Well it does but why has it to "overwrite" the settings I defined for this accordion?
I also thought about the possibility that it might be wrong to create the accordion on an empty <div> so I tested it with a given entry and added some elements afterwards.
But the jsfiddle shows exactly the same results.
In a recent SO thread I found someone who basically does the same thing as I do but in his jsfiddle he faces the same "issue".
He adds a new panel and the first panel opens after the refresh.
My current solution for this issue is to destroy the accordion and recreate it each time there's new content for it.
But this seems quite rough to me and I thought the refresh method solves the need to destroy the accordion each time new content gets applied.
See the last jsfiddle
$(document).ready(function () {
//variable to show "new" content gets appended correctly
var foo = 1;
$('#clickMe').on('click', function () {
var data = '';
for (var i = 0; i < 3; i++) {
data += '<h3>title' + foo + '</h3><div>content</div>';
foo++;
}
if ($('#accordion').hasClass('ui-accordion')) {
$('#accordion').accordion('destroy');
}
$('#accordion').empty().append(data).accordion({
collapsible: true,
active: false,
heightStyle: "content"
});
});
});
Unfortunately it is not an option for me to change the content of the given 3 entries because the amount of panels varies.
So my questions are the one in the title and if this behaviour is wanted like that or if anybody faces the same problem?
For the explanation of this behaviour, have a look in the refresh() method of the jquery-ui accordion widget, the problem you are facing is at line 10 :
refresh: function() {
var options = this.options;
this._processPanels();
// was collapsed or no panel
if ((options.active === false && options.collapsible === true) || !this.headers.length) {
options.active = false;
this.active = $();
// active false only when collapsible is true
} if (options.active === false) {
this._activate(0); // <-- YOUR PROBLEM IS HERE
// was active, but active panel is gone
} else if (this.active.length && !$.contains(this.element[0], this.active[0])) {
// all remaining panel are disabled
if (this.headers.length === this.headers.find(".ui-state-disabled").length) {
options.active = false;
this.active = $();
// activate previous panel
} else {
this._activate(Math.max(0, options.active - 1));
}
// was active, active panel still exists
} else {
// make sure active index is correct
options.active = this.headers.index(this.active);
}
this._destroyIcons();
this._refresh();
}

Keep jQuery tooltip open on click?

I want to use jQuery UI's tooltip feature, however I need it so when you click an element (in my case an image) the tool tip stays open. Can this be done? I couldn't see any options for this.
http://api.jqueryui.com/tooltip/
UPDATE here is my code. I thought the 4th line should work but sadly not:
HTML
<img class="jqToolTip" src="/query.gif" title="Text for tool tip here">
Javascript
$('.jqToolTip').tooltip({
disabled: false
}).click(function(){
$(this).tooltip( "open" );
// alert('click');
}).hover(function(){
// alert('mouse in');
}, function(){
// alert('mouse out');
});
I was trying to solve the same exact problem, and I couldn't find the answer anywhere. I finally came up with a solution that works after 4+ hours of searching and experimenting.
What I did was this:
Stopped propagation right away if the state was clicked
Added a click handler to track the state
//This is a naive solution that only handles one tooltip at a time
//You should really move clicked as a data attribute of the element in question
var clicked;
var tooltips = $('a[title]').on('mouseleave focusout mouseover focusin', function(event) {
if (clicked) {
event.stopImmediatePropagation();
}
}).tooltip().click(function() {
var $this = $(this);
var isOpen = $this.data('tooltip');
var method = isOpen ? 'close' : 'open';
$this.tooltip(method);
//verbosity for clarity sake, yes you could just use !isOpen or clicked = (method === 'open')
if (method === 'open') {
clicked = true;
} else {
clicked = false;
}
$this.data('tooltip', !isOpen);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/themes/redmond/jquery-ui.css" rel="stylesheet" />
Tooltips
Hopefully this will help a future googler.
Thanks in part to this post
http://api.jqueryui.com/tooltip/#method-open
$('img.my-class').click(function() {
$(this).tooltip( "open" );
}

jquery-ui sortable | How to get it work on iPad/touchdevices?

How do I get the jQuery-UI sortable feature working on iPad and other touch devices?
http://jqueryui.com/demos/sortable/
I tried to using event.preventDefault();, event.cancelBubble=true;, and event.stopPropagation(); with the touchmove and the scroll events, but the result was that the page does not scroll any longer.
Any ideas?
Found a solution (only tested with iPad until now!)!
https://github.com/furf/jquery-ui-touch-punch
To make sortable work on mobile.
Im using touch-punch like this:
$("#target").sortable({
// option: 'value1',
// otherOption: 'value2',
});
$("#target").disableSelection();
Take note of adding disableSelection(); after creating the sortable instance.
The solution provided by #eventhorizon works 100%.
However, when you enable it on phones, you will get problems in scrolling in most cases, and in my case, my accordion stopped working since it went non-clickable. A workaround to solve it is to make the dragging initializable by an icon, for example, then make sortable use it to initialize the dragging like this:
$("#sortableDivId").sortable({
handle: ".ui-icon"
});
where you pass the class name of what you'd like as an initializer.
Tom,
I have added following code to mouseProto._touchStart event:
var time1Sec;
var ifProceed = false, timerStart = false;
mouseProto._touchStart = function (event) {
var self = this;
// Ignore the event if another widget is already being handled
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
return;
}
if (!timerStart) {
time1Sec = setTimeout(function () {
ifProceed = true;
}, 1000);
timerStart=true;
}
if (ifProceed) {
// Set the flag to prevent other widgets from inheriting the touch event
touchHandled = true;
// Track movement to determine if interaction was a click
self._touchMoved = false;
// Simulate the mouseover event
simulateMouseEvent(event, 'mouseover');
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
// Simulate the mousedown event
simulateMouseEvent(event, 'mousedown');
ifProceed = false;
timerStart=false;
clearTimeout(time1Sec);
}
};
The link for the top-voted Answer is now broken.
To get jQuery UI Sortable working on mobile:
Add this JavaScript file to your project.
Reference that JS file on your page.
For more information, check out this link.

Resources