Apply callback to all instances of Magnific Popup? - magnific-popup

Is there a way to bind an event callback so that it's called for all instances of Magnific Popup? On a site I'm building several elements have fixed position and "jump" when the main browser scrollbar disappears. I want to set a callback to add padding for those elements before any popup is opened, and remove the padding when the popup is closed. I don't want to re-use the callbacks every time I initialize Magnific Popup – I'd rather set one set of callbacks that is run every time Magnific opens and closes a popup.
My first thought was to use event delegation and bind pretty high in the document tree. I was trying to bind to the mfpOpen event and listen on the BODY tag, but it doesn't seem like the event bubbles. Is there another solution?

Events are dispatched only on target element or on document (if target element is not present).
But you may override open function globally using prototype, e.g.:
$.magnificPopup.instance.open = function(data) {
console.log('before open anything');
$.magnificPopup.proto.open.call(this,data);
console.log('after open anything');
};
You may do the same thing with close function (it doesn't have any arguments).

Related

Vuetify dialog adds new dialog to dom each time it is opened and exon't remove the previous on close

We have a large scale application but in Vue 2, Composition API and Nuxt.
However we load a lot of content in dialogs. After using the application for several minutes you can see a build up of these dialogs in the DOM.
Is there a way for Vuetify to use the existing dialog one instead of creating a new instance.
Or remove the existing one on close?
I can't see anything in the docs or similar issues from other users.
Each v-dialog with a v-model will be added to the DOM first time it's active/opened, then the visibility is changed on subsequent toggles. It's more "expensive" to add and remove DOM elements (size depending on its nested content) than toggle an active class and add a simple overlay. Probably why it's not recommended to nest v-dialog within a v-for loop, the DOM will get too crowded. If the dialog content is interchangeable, you could have one dialog on the page where the content is toggled too.

How to prevent selection of a SortableJS item when clicking on one of its child elements?

The Issue
I'm using SortableJS to build a draggable tree component. Which means each of my sortable-items has a toggle-arrow as a child element that opens and closes a sub-tree (if there is one).
I'm attempting to use stopPropagation() to prevent the selection of the parent sortable-item if the toggle-arrow is clicked, but it's not working.
It looks like this when closed:
And looks like this when open:
The blue highlight you see in the open state (the second image) is the styling I've chosen for the selectedClass option when using the multiDrag plugin.
This is illustrating that when I click on the toggle-arrow it results in the parent sortable-item being selected.
I don't want this to happen.
The Code
The code for an item in my SortableJS tree component looks like so (using Vue.js, and Pug syntax):
div.sortable-item
div.content
div.toggle-arrow(#click.stop="toggleTree($event)")
div.icon
div.title
div.sub-tree
And then I've got a handler for the #click binding on my toggle-arrow element:
toggleTree = function($event) {
$event.stopPropagation()
/// Code for handling the sub-tree toggling goes here.
/// The sub-tree toggling itself works just fine.
}
You can see that I'm declaring #click.stop as the event binding, which should stop the click event from bubbling up from the toggle-arrow child element, but it's not working.
I'm even attempting to use $event.stopPropagation within the handler. But, the event seems to continue to bubble, and thus the parent sortable-item element ends up in a selected state.
I've also tried declaring #click.native.stop as the event binding, but it simply prevents my toggleTree method from firing at all. I'm assuming there's another event handler somewhere within SortableJS that's interfering with the #click.native.stop binding.
Questions
How do I stop propagation of an event when a child element of my sortable-item is clicked?
How is selection handled by the multiDrag plugin? I dug through the code and saw that the select event is fired within the handler of the drop event of the sortable-item, but I'm confused by that. Why is the drop event handler being used to toggle selection of a sortable-item?
Thanks in advance for any light you may be able to shed on this.
Wrong Event
Looking at the source of SortableJS it seems that the event you want to stop from bubbling is not the click event, but rather the mouseup event.
The "Stuck" Drag Item Problem
As indicated in the comments of this answer, stopping propagation on the mouseup event causes an issue where the drag is started unintentionally, and the sortable-item becomes "stuck" to the pointer.
It seems that the "drag initiation" is triggered by either pointerdown, mousedown, or touchstart events, depending on the device.
It can be safely assumed that the pointerdown event is the one that does the triggering according to caniuse.com.
The Solution
So the actual way to solve this is to use a #pointerdown.stop event binding to trigger your toggleTree method without triggering either selection of the sortable-item, or the unintentional drag initiation.
div.sortable-item
div.content
div.toggle-arrow(#pointerdown.stop="toggleTree($event)")
div.icon
div.title
div.sub-tree
Change
div.toggle-arrow(#click.stop="toggleTree($event)")
to
div.toggle-arrow(#click.native.stop="toggleTree($event)")
If all you did in toggleTree was stopPropagation, you could have changed it to:
div.toggle-arrow(#click.native.stop)
Docs.
In short, you're currently stopping propagation on any emitted clicks from the child component (a.k.a. custom Vue event, which doesn't actually need propagation stopped as it doesn't bubble by default). What you want to do is call event.stopPropagation() on the native click event.
An alternative would be to use:
div.toggle-arrow(#click.native="toggleTree($event)")
... and call .stopPropagation() inside toggleTree. Which is precisely what .stop modifier does.

Seaside calling a component inside javascript

I have a seaside application with a master-detail page. The master page has a table that consists of a list of tr records. When the user clicks a particular tr element, I want to call a detail component, which'll show the individual record's data.
Since I cannot make a tr element with callback or have it contain an anchor with a callback, I want the tr's onClick property to have some JavaScript which'll call: subcomponent . When I tried this, I got an error saying call: can only be used in callbacks and tasks.
Using ajax is a workaround, however it breaks the back button.
Edit:
More generally, I'd like to know how to set callback like behaviour for various JavaScript events.
Well, you cannot render a component in a tr element, but you could add some anchor or other element in one of its td children.
For my project I did roughly the following: I added an anchor to each row with a special css class, e.g. '.dblclick-action'. This anchor has a normal Seaside callback.
Then I bound a dblclick handler to the tr element that does something like document.location=$(this).find('.dblclick.ction').get(0).href;
I am not close to a Smalltalk image now to give you source code, but I hope you get the idea: you don't use Ajax to click the link in that particular row, but instead have the browser navigate to the callback that is associated to the link in that row. You could say you use the tr.'s dblclick handler to click the link and then let the normal Seaside stuff do its work. No magic there. You can find a little bit more info here.
If you don't want the anchor to be visible you may want to experiment with making the anchor invisible (display: none) or the like.
If you are a bit more experiment friendly, you can also try saving a callback on the server and render its url with callback id as an attribute of the tr element and use the dblclick handler to follow the link from that attribute you extract the value of an attribute in query using attr().
I forgot to answer your initial question: you cannot issue a call: from javascript. But you can use the document.location trick to re/misuse an existing link to a callback on the page using the technique I described in my first answer.

Chrome Extension Dev: Updating javascript variables in background.html

I have a working extension, and now I'm trying to add some options/features!
I have a background page which keeps track of the "state" of each tab (per tab javascript variables that hold options/settings). Now I want to have a popup.html file which will have 1 option, a time slider. I'm not concerned with the slider or any html/css. My issue is that I'm unsure of how to communicate the new setting to the background page.
My background page contains some code like this:
chrome.browserAction.onClicked.addListener(function(tab) {
Init(tab);
});
...
function Init(tab)
{
chrome.tabs.sendRequest(tab.id,
{
'TurnOffTimer': false,
'TimerIsOn': TimerIsOn,
'Interval': Interval,
'RefreshRate': RefreshRate
}, responseCallback);
}
TimerIsOn, Interval, and RefreshRate are javascript variables on the same page.
What I need to know is how, once the slider (which is declared in the popup.html) is set and the user clicks 'ok', the timer value can be sent to background.html to be stored in the appropriate javascript variable, and the extension functionality can be updated. I can create another function called Update which will take this updated value and run, but I need to know how I can call the Update function from popup.html if it's declared in background.html.
Hopefully I'm making sense but if clarification is necessary, feel free to ask.
You can directly access background page's window object from popup with chrome.extension.getBackgroundPage(). So to change some var in bg page from popup you can just call:
chrome.extension.getBackgroundPage().Interval = 5;
You would need to change your extension a bit, as once you have popup attached to browser action button, chrome.browserAction.onClicked listener won't be firing anymore. You would need to call Init from popup manually in a similar way:
chrome.extension.getBackgroundPage().Init();
And inside Init() manually get selected tab id, as it won't be passed anymore.

What's the difference between .select, .show, and .load in jquery-ui tabs?

I see that there are three events fired when a tab is selected and I see the order in which the events are fired but I'm rather confused about which event to use or how these are truly different. If all three are fired, couldn't I just put my code in any of the events?
I must be missing something here. Can someone clarify?
So after messing with this more I'm going to share what I ended up doing in the hopes that it might help someone else.
I had to generate dynamic tabs based on data returned in an Ajax call. It's basically data by date where the tabs are the date and they display whatever data falls within that date.
Generating the tabs from the returned data was easy but I couldn't figure out how to write out the associated data. Finally (and I should have started here), I looked that the generated dom and noticed that a dynamically created tab also creates a div. Maybe that's obvious to some (it wasn't to me) and if it was in the documentation I missed it. Anyway, this code will generate tabs from an array and then append html to the associated div when the tab is clicked. I don't need all the variables but I thought that might make it more readable. Put the function for show before adding the tabs or it wont work!
var _sessionDates = getSessionDates(sessionData.Sessions);
var $tabs = $("#sub-tabs");
$tabs.tabs({
show: function(event, ui) {
var selected = $tabs.tabs('option', 'selected');
var _sessionDates = getSessionDates(sessionData.Sessions);
var grid = buildGrid(_sessionDates[selected]);
$('#' + _sessionDates[selected]).html(grid);
}
});
$(_sessionDates).each(function(i, dayOfShow) {
var d = dateFormat(dayOfShow, "mediumDate");
$tabs.tabs('add', '#' + dayOfShow, d);
});
Finally, I have to "scroll" through my data shown in the tab and I was able to do that with these two lines. The first line gives me the id of the div element corresponding to the selected tab (which is really the important part) and the second line just calls my method and passes in the id of the div less the '#'. My date is also my id. There's a global variable that I've changed outside of this that makes it work. I know that's bad and I'll remove it when I refactor it.
var $el = $($('#sub-tabs a')[$('#sub-tabs').tabs('option', 'selected')]).attr('href');
$($el).html(buildGrid($el.replace('#', '')));
select is triggered when a tab is clicked on, but before the tab has been shown
load is triggered after the contents of a remote tab have been loaded (i.e. a tab whose content is not part of the initial page payload, but that is loaded via an ajax call when the user clicks on the related tab)
show is triggered after a tab has been shown
The jQuery documentation makes this relatively clear, but I had to experiment in order to fully understand the difference between select and show.
This has been made more clear since the original question was asked so now the following is true:
Effectively, the functionality for "show" is now provided by "activate" and "select" is done with "beforeActivate" while "load" is still the same.
activate: (previously show) Triggered after a tab has been activated (after animation completes)
beforeActivate: (previously select) Triggered immediately before a tab is activated.
load: Triggered after a remote tab has been loaded.
Also provided are:
beforeLoad: Triggered when a remote tab is about to be loaded, after the beforeActivate event.
create: Triggered when the tabs are created.