ddlab
ddlab

Reputation: 930

jQuery, apply function automatically on loaded elements

I know that I can fire functions after ajax call using the success option, e.g. to manipulate loaded content, like

$.ajax({
    url : url,
    ...
    success: function(data, textStatus, jqXHR){
        alert($('#overlay > #popup input').length);
    },
    ...
});

I also know that I can apply interactive events like click on ajax loaded content via

$(document).on('click','#overlay > #popup',function(e) {
    alert($(e.target).find('input').length);
});

which works ofcourse.

I try to find a way, do the same alert automatically, only after content has been loaded, instead of click on it, like this (sorry for this bad NON-working example)

$(document).on('load','#overlay > #popup',function(e) {
    alert($(e.target).find('input').length);
});

EXAMPLE for explaination, not part of the question

At the end I want to apply calculated styles to loaded input[type=radio] using jQuery (individual width, depending on its value attribute, to be displayed automatically in css pseudo element ::after {content: attr(value);} )

The current html output from php function is

<label class="fl" style="width: 80px;">
    <input type="radio" name="' . $name . '" value="' . $year . '" ' . ($year == date('Y')?'checked="checked"':'') . '/>
</label>

the current result looks like this

four styled input type radio

where the visible captions are the ::after elements. Only I want - in case of different value length - the width is set automatically, not hard coded.

Can somebody open this node in my head ?

Upvotes: 3

Views: 175

Answers (2)

ddlab
ddlab

Reputation: 930

SOLUTION based on @CertainPerformance 's answer

$(document).on('ajaxComplete', function(e){
    var i = $('#overlay > #popup').find('input[type="radio"],input[type="checkbox"]');
    i.each(function(index){
        var el = $(this);
        var l = el.closest('label');
        var v = el.attr('value');
        var fs = el.css('font-size');
        $('body').prepend($('<span></span>').attr('id','st'+index).addClass('st').html(v).css({fontSize:fs}));
        var testel = $('body > span#st'+index);
        var w = testel.outerWidth();
        l.css({width:w+30}); /* +30 instead of a padding */
    });
    $('body > span.st').remove();
});

Works exactly as I want. And the entire thing saves ajax payload, as intended.

An here's the result (with nonsense content, for demo only)

input radio with nonsense content, for demo only

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370679

If you know which element is going to be appended to on ajax complete, you can attach a MutationObserver to it. When the observer fires, check to see if the element you're curious about exists as a descendant:

new MutationObserver((_, observer) => {
  if (popup.querySelector('input')) {
    console.log('Input descendant now exists!');
    // Don't keep the observer active once it's served its purpose:
    observer.disconnect();
  }
}).observe(popup, { childList: true });

// example of asynchronous ajax call
setTimeout(() => {
  popup.innerHTML = '<div>popup content<input></div>';
}, 2000);
<div id="overlay">
  <div id="popup">
  </div>
</div>

But while this is possible, it's pretty bad practice. If you know when an element is going to be created, it'd be much better to put all the functionality for that inside the ajax success callback, rather than use MutationObserver (which should generally be reserved only for situations when other options are impossible, like when you don't have control over the code).

For elements loaded after a network request, you can also use ajaxComplete with jQuery:

const callback =  () => {
  if (popup.querySelector('input')) {
    console.log('Input descendant now exists!');
    // Don't keep the observer active once it's served its purpose:
    $(document).off('ajaxComplete', callback);
  }
};
$(document).on('ajaxComplete', callback);

// example of asynchronous ajax call
$.get({
  url: 'https://jsonplaceholder.typicode.com/todos/1',
  success() {
    popup.innerHTML = '<div>popup content<input></div>';
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="overlay">
  <div id="popup">
  </div>
</div>

Upvotes: 1

Related Questions