Preferred way of modifying elements that have yet to be created (besides events)

后端 未结 5 1494
孤独总比滥情好
孤独总比滥情好 2020-12-23 11:49

There are a lot of questions about binding future manipulations to non-existent elements that all end up answered with live/delegate. I am

相关标签:
5条回答
  • 2020-12-23 11:55

    Well, first of all, you shouldn't even be using selectors like this if you're worried about perf.

    $('input[type=text], input[type=password], textarea, .basic_form .block select, .order_form .form_item select, .order_form .form_item input')
    

    Any browser that doesn't have native implementations of xpath (more than just IE iirc) or getElementsByClassName (IE 7 and below) could easily spend a few seconds chewing on that on a big site so polling would of course be completely out of the question if you want it that broad.

    0 讨论(0)
  • 2020-12-23 12:07

    I was reading up on the new release of jQuery, version 1.5 and I immediately thought of this question.

    With jQuery 1.5 you can actually create your own version of jQuery by using something called jQuery.sub();

    That way you can actually override the default .append(), insert(), .html(), .. functions in jQuery and create your own custom event called something like "mydomchange" - without it affecting all other scripts.

    So you can do something like this (copied from the .sub() documentation with minor mod.):

    var sub$ = jQuery.sub();
    sub$.fn.insert = function() {
        // New functionality: Trigger a domchange event
        this.trigger("domchange");
        // Be sure to call the original jQuery remove method
        return jQuery.fn.insert.apply( this, arguments );
    };
    

    You would have to do this to all the dom manipulation methods...

    jQuery.sub() in the jQuery documention: http://api.jquery.com/jQuery.sub/

    0 讨论(0)
  • 2020-12-23 12:09

    In my opinion, the DOM Level 3 events DOMNodeInsertedhelp (which fires only for nodes) and DOMSubtreeModifiedhelp (which fires for virtually any modification, like attribute changes) are your best shot to accomplish that task.

    Of course, the big downside of those events is, that the Internet Explorers of this world don't support them
    (...well, IE9 does).

    The other reasonable solution for this problem, is to hook into any method Which can modify the DOM. But then we have to ask, what is our scope here?

    Is it just enough to deal with DOM modification methods from a specific library like jQuery? What if for some reason another library is modifying the DOM or even a native method ?

    If it's just for jQuery, we don't need .sub() at all. We could write hooks in the form of:

    HTML

    <div id="test">Test DIV</div>
    

    JS

    (function(_append, _appendTo, _after, _insertAfter, _before, _insertBefore) {
        $.fn.append = function() {
            this.trigger({
                type: 'DOMChanged',
                newnode: arguments[0]
            });
            return _append.apply(this, arguments);
        };
        $.fn.appendTo = function() {
            this.trigger({
                type: 'DOMChanged',
                newnode: this
            });
            return _appendTo.apply(this, arguments);
        };
        $.fn.after = function() {
            this.trigger({
                 type: 'DOMChanged',
                 newnode: arguments[0]
             });
            return _after.apply(this, arguments);
        };
    
        // and so forth
    
    }($.fn.append, $.fn.appendTo, $.fn.after, $.fn.insertAfter, $.fn.before, $.fn.insertBefore));
    
    $('#test').bind('DOMChanged', function(e) {
        console.log('New node: ', e.newnode);
    });
    
    $('#test').after('<span>new span</span>');
    $('#test').append('<p>new paragraph</p>');
    $('<div>new div</div>').appendTo($('#test'));
    

    A live example of the above code can be found here: http://www.jsfiddle.net/RRfTZ/1/

    This of course requires a complete list of DOMmanip methods. I'm not sure if you can overwrite native methods like .appendChild() with this approach. .appendChild is located in Element.prototype.appendChild for instance, might be worth a try.

    update

    I tested overwriting Element.prototype.appendChild etc. in Chrome, Safari and Firefox (official latest release). Works in Chrome and Safari but not in Firefox!


    There might be other ways to tackle the requirement. But I can't think of a single approach which is really satisfying, like counting / watching all descendents of a node (which would need an interval or timeouts, eeek).

    Conclusion

    A mixture of DOM Level 3 events where supported and hooked DOMmanip methods is probably the best you can do here.

    0 讨论(0)
  • 2020-12-23 12:10

    Great question

    There seems to be a custom event you can bind: http://javascript.gakaa.com/domnodeinserted-description.aspx

    So I guess you could do something like:

    $(document).bind('DOMNodeInserted',function(){ /* do stuff */ });
    

    But I haven't tried so I don't have a clue..

    btw.: related question: Can javascript listen for "onDomChange" on every Dom elements?

    0 讨论(0)
  • 2020-12-23 12:14

    There is no simple obvious way to do it. The only surefire approach is active polling, which causes there to be a render hiccup between when the new element is created and when the polling notices it. That can also make your page take a lot of resources depending on how frequently you poll the page. You can also couple this, as you observed, with binding several browser-specific events to at least make things work out better in those browsers.

    You can override jQuery's DOM modification functions to trigger a custom change event (and use $.live to catch those events for manipulation), but when I've tried this in the past, it's subtly broken various jQuery plugins (my guess is some of those plugins do something similar). In the end I've given up on doing so reliably since I don't want to give up the performance and render hiccups to active polling, and there is no other comprehensive way to do it. Instead I have an initialization event I make sure to trigger for each DOM change I make, and I bind my manipulation events to those instead.

    Be careful, it's easy to get stuck in an infinite event loop if you don't think things through, and this can also be subtle and difficult to track down; and worse yet may happen for a corner case your unit testing didn't allow for (so your users experience it instead of just you). The custom manually triggered initialization event is easier to diagnose in this sense since you always know exactly when you're triggering it.

    0 讨论(0)
提交回复
热议问题