2

I've got JQuery UI widget that appends DOM element to do the job. The widget removes this element in _destroy method so it will clean-up after itself.

Everything is good until I try to replace widget with some other widget. Original widget gets removed (destroy method will remove widgets custom element), but new widget is not appended.

Best description is code so here is example: http://jsfiddle.net/71xdxLvp/3/

The question is: How to replace my widget?

Note:

I know where the problem is, but i don't know how to solve it.

Problem is in jquery.js in method replaceWith - see comments in code:

return this.each(function() {
    var next = this.nextSibling, // nextSibling is widgets custom element
    parent = this.parentNode;

    // Widget is removed and the widgets custom element is removed too
    jQuery( this ).remove();

    if ( next ) {
        // New widget should be prepended to the widgets custom element, but  
        // the element is already removed from DOM. 
        // Method before() check this and won't append new widget
        jQuery(next).before( value );
    } else {
        jQuery(parent).append( value );
    }
});

Edit:

replaceWith is called by third-party framework Apache Wicket

widget which creates DOM element is CodeMirror

1 Answer 1

1

You've stumbled onto an obscure corner case with .replaceWith() which results in a bug. What happens is:

  • by destroying the first node you also destroy the following sibling yourself
  • .replaceWith calls that destroy function, then tries to insert a new node before the now non-existent sibling
  • it throws a NotFoundError on the missing node

You can work around this fairly simply if you "simulate" what replaceWith does yourself: first append the new element, and only after that call remove:

$("#widget").next().before("<div id=\"secondWidget\"></div>");
$("#widget").remove();

Fiddle: http://jsfiddle.net/w7Lqfzcc/1/


If this is not an option (as mentioned in the comment) you can do something slightly different: instead of calling .remove directly, just add a class with display:none, and clean it up later:

this.widgetContent.addClass("inactive");

.widgetContent.inactive { display:none; }

Fiddle: http://jsfiddle.net/w7Lqfzcc/3/ [note I replaced the id with a class in the fiddle; using id-s for generated child elements doesn't make much sense]

This is messy, but it's the simplest way to tackle the awkward widget-generated-sibling structure you have. If you feel like cleaning it up, using some sort of widget-level container is the first thing you should look into.

Sign up to request clarification or add additional context in comments.

3 Comments

Unfortunately .replaceWith is called by third party library (Wicket) so I can't simulate it by myself. :(
I see; check if the edit helps. I'd generally recommend reviewing your widget's structure, as replaceWith is probably not the only function to rely on siblings
Thank you for update - it's a messy way, but i think it can work. Widget is also third-party (see my edit) so I can't easily modify it's structure.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.