Skip to content

Commit

Permalink
Fix primefaces#11700: Widgets with trigger/target bind for removal
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Apr 7, 2024
1 parent 9b8e282 commit 87312d0
Show file tree
Hide file tree
Showing 12 changed files with 51 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ PrimeFaces.widget.BlockUI = PrimeFaces.widget.BaseWidget.extend({
}

this.bindResizer();
// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.target);
},

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,6 @@ PrimeFaces.widget.ColumnToggler = PrimeFaces.widget.DeferredWidget.extend({
else
$this.show();
});
// if the trigger/table is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.trigger);
this.bindDomRemovalEvent(this.table);

//checkboxes
this.itemContainer.find('> .ui-columntoggler-item > .ui-chkbox > .ui-chkbox-box').on('mouseenter.columnToggler', function() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ if (!PrimeFaces.widget) {
* @prop {PrimeFaces.widget.DestroyListener<BaseWidget>[]} destroyListeners Array of registered listeners invoked
* when this widget is destroyed. You should normally not use modify this directly, use {@link addDestroyListener}
* instead.
* @prop {string[]} removalIdentifiers Array of registered DOM ID's that if they are removed from the DOM this
* widget is considered "detached".
* @prop {string | string[]} id The client-side ID of this widget, with all parent naming containers, such as
* `myForm:myWidget`. This is also the ID of the container HTML element for this widget. In case the widget needs
* multiple container elements (such as {@link Paginator}), this may also be an array if IDs.
Expand Down Expand Up @@ -322,12 +324,13 @@ if (!PrimeFaces.widget) {
this.widgetVar = cfg.widgetVar;
this.destroyListeners = [];
this.refreshListeners = [];
this.removalIdentifiers = [];

//remove script tag
this.removeScriptElement(this.id);

// clean up the widget if its DOM element is removed from the DOM
this.bindDomRemovalEvent(this.jq);
this.destroyOnElementRemoval(this.jq);
},

/**
Expand Down Expand Up @@ -413,12 +416,20 @@ if (!PrimeFaces.widget) {
* @return {boolean} `true` if this widget is currently detached, or `false` otherwise.
*/
isDetached: function() {
var element = document.getElementById(this.id);
if (typeof(element) !== 'undefined' && element !== null) {
return false;
if (this.removalIdentifiers) {
for (var i = 0; i < this.removalIdentifiers.length; i++) {
var id = this.removalIdentifiers[i];
var element = document.getElementById(id);
if (typeof (element) === 'undefined' || element === null) {
// If any ID is not found, return true immediately
return true;
}
}
}
this.removalIdentifiers = [];

return true;
// If all IDs are found, return false
return false;
},

/**
Expand Down Expand Up @@ -605,11 +616,19 @@ if (!PrimeFaces.widget) {
* @param {JQuery | HTMLElement} watchElement The HTML element that if removed from the DOM will detach this widget.
* @since 14.0.0
*/
bindDomRemovalEvent: function(watchElement) {
if (this.widgetVar) {
destroyOnElementRemoval: function(watchElement) {
if (this.widgetVar && watchElement) {
var $this = this,
namespace = '.widget' + this.id;
$(watchElement).off("remove" + namespace).on("remove" + namespace, function() {
namespace = '.widget' + this.id,
$watchElement = $(watchElement),
watchId = $watchElement.attr('id');

// add id to list of ids checked for isDetached
if (!this.removalIdentifiers.includes(watchId)) {
this.removalIdentifiers.push(watchId);
}
// subscribe to the remove event and add this to detached widgets if DOM has been removed
$watchElement.off("remove" + namespace).on("remove" + namespace, function() {
if (!PrimeFaces.detachedWidgets.includes($this.widgetVar)) {
PrimeFaces.detachedWidgets.push($this.widgetVar);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ PrimeFaces.widget.Draggable = PrimeFaces.widget.BaseWidget.extend({
this.jqTarget.draggable(this.cfg);

// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.jqTarget);
this.destroyOnElementRemoval(this.jqTarget);

this.addDestroyListener(function() {
if ($this.jqTarget.length) {
Expand Down Expand Up @@ -109,7 +109,7 @@ PrimeFaces.widget.Droppable = PrimeFaces.widget.BaseWidget.extend({

this.bindDropListener();
// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.jqTarget);
this.destroyOnElementRemoval(this.jqTarget);

this.jqTarget.droppable(this.cfg);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ PrimeFaces.widget.KeyFilter = PrimeFaces.widget.BaseWidget.extend({
this.applyKeyFilter(nestedInput, cfg);
}
// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.target);
this.destroyOnElementRemoval(this.target);
},

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ PrimeFaces.widget.Menu = PrimeFaces.widget.BaseWidget.extend({
this.trigger = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.jq, this.cfg.trigger);

// if the trigger is removed from the DOM we should destroy this menu widget
this.bindDomRemovalEvent(this.trigger);
this.destroyOnElementRemoval(this.trigger);

//mark trigger and descendants of trigger as a trigger for a primefaces overlay
this.trigger.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,6 @@ PrimeFaces.widget.ContextMenu = PrimeFaces.widget.TieredMenu.extend({
//target
this.jqTargetId = documentTarget ? document : PrimeFaces.escapeClientId(this.cfg.target);
this.jqTarget = $(this.jqTargetId);

// if the target is a regular element (aka not the document) and it is removed
// from the DOM we should destroy this widget
if (this.cfg.target && this.jqTarget.length) {
this.bindDomRemovalEvent(this.jqTarget);
}

//append to body
this.cfg.appendTo = '@(body)';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,6 @@ PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.DynamicOverlayWidget.extend({
});

this.bindAutoHide();

// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.target);
},

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ PrimeFaces.widget.Resizable = PrimeFaces.widget.BaseWidget.extend({
this.jqTarget = $(PrimeFaces.escapeClientId(this.cfg.target));

// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.jqTarget);
this.destroyOnElementRemoval(this.jqTarget);

this.renderDeferred();
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ PrimeFaces.widget.Spotlight = PrimeFaces.widget.BaseWidget.extend({
init: function(cfg) {
this._super(cfg);
this.target = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.jq, this.cfg.target);

// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.target);

if(!$(document.body).children('.ui-spotlight').length) {
this.createMasks();
Expand All @@ -42,7 +39,8 @@ PrimeFaces.widget.Spotlight = PrimeFaces.widget.BaseWidget.extend({
* @private
*/
createMasks: function() {
$(document.body).append('<div class="ui-widget-overlay ui-spotlight ui-spotlight-top ui-helper-hidden"></div><div class="ui-widget-overlay ui-spotlight ui-spotlight-bottom ui-helper-hidden"></div>' +
var documentBody = $(document.body);
documentBody.append('<div class="ui-widget-overlay ui-spotlight ui-spotlight-top ui-helper-hidden"></div><div class="ui-widget-overlay ui-spotlight ui-spotlight-bottom ui-helper-hidden"></div>' +
'<div class="ui-widget-overlay ui-spotlight ui-spotlight-left ui-helper-hidden"></div><div class="ui-widget-overlay ui-spotlight ui-spotlight-right ui-helper-hidden"></div>');
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,31 @@ PrimeFaces.widget.Sticky = PrimeFaces.widget.BaseWidget.extend({
};

// if the target is removed from the DOM we should destroy this widget
this.bindDomRemovalEvent(this.target);
this.destroyOnElementRemoval(this.target);
this.bindEvents();
}
},


/**
* @override
* @inheritdoc
*/
destroy: function() {
this._super();
if (this.ghost && this.ghost.length) {
this.ghost.remove();
}
},

/**
* @override
* @inheritdoc
* @param {PrimeFaces.PartialWidgetCfg<TCfg>} cfg
*/
refresh: function(cfg) {
this.target = $(PrimeFaces.escapeClientId(this.cfg.target));
// if the target is removed from the DOM we should destroy this widget
this.destroyOnElementRemoval(this.target);

if (this.fixed) {
this.ghost.remove();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ PrimeFaces.widget.Tooltip = PrimeFaces.widget.BaseWidget.extend({
}
else {
// GitHub #9941 Helper to remove tooltips when elements are removed
this.bindDomRemovalEvent(this.target);
this.destroyOnElementRemoval(this.target);

this.target.off(this.cfg.showEvent + ' ' + this.cfg.hideEvent)
.on(this.cfg.showEvent, function(e) {
Expand Down

0 comments on commit 87312d0

Please sign in to comment.