Support

Account

Home Forums Feature Requests JS hooks – a reference to DOM node of the rendered item in the callback function

Solving

JS hooks – a reference to DOM node of the rendered item in the callback function

  • I’ve been playing with JS hooks lately. I wanted to parse image EXIF data when the user uploads a new image. I found that I can hook into ‘change’ event. But sadly, the only thing that is passed as an argument to the callback function is $(this) – a reference to the input.

    It would be great if the reference to the rendered element was passed as a second argument. It would be even better if the previous input value was passed as an argument as well.

    What do I mean by a reference to the rendered item? Some ACF fields (for example image field, google maps, field) have a hidden input, so the value passed to the callback function is a reference to the hidden input.

    If I want to get an image when the ACF field value is changed, I can listen for ‘change’ event, but in the callback function I get only a hidden input reference. To get the image I have to make some DOM traversing, for instance: input.closest('.acf-image-uploader').find('img')
    Which is neither elegant nor bulletproof (it can break after ACF update, if the DOM structure is changed)

  • I found more issues. this in the callback function is a reference to window. IMO the field settings (including field name and field type) should be attached to this.

    In the current implementation, this hook isn’t very helpful. It requires too much effort to determine which field’s value was changed. The best way at the moment to find which field is it is to check e.context.name which returns a string like this: acf[field_56d99d7de115e].

    // Edit:

    An example:

    
    var imageInputOnChange = function ($input) {
    
        // This is the only way to check what field has been changed.
        if ($input.context.name !== 'acf[field_56d99d7de115e]') {
            return;
        }
    
        // Finding the image requires DOM traversing
        var $image = $input.closest('.acf-image-uploader').find('img');
        
       // Do something with the $image.
    };
    
    acf.add_action('change', imageInputOnChange);
    
  • I am currently working on a project that has required me to dig deeply into the JS side of ACF. what I’m doing is extending the acf.ajax object/class. Some things can also be done by extending the acf.field object/class. You can see examples of this by looking at the JS files in ACF. You can look that what I’ve been doing here https://github.com/Hube2/blunt-parametric-search/blob/master/admin/js/admin.js. It’s still pretty rough and I’m just putting everything in one place. I’m extending the acf.ajax because I make several AJAX requests that dynamically load values field values.

  • So you see my point 🙂

    There’s a lot of DOM traversing in your code. But the issue is not only related to the lack of data in the ACF callbacks but also to the way how ACF JS code is designed – it relies strongly on DOM.

    It would be great if ACF code relied on a data model instead of DOM. But it would require a strong ACF core refactoring. I wish Backbone models and collections were used.

  • Somewhere, at some point, the DOM must be traversed in order to make changes to the page. Using some other system will simply abstract it away. At least the way it is I can inspect the elements of the page and see clearly what it is I need to do, get, find without adding another layer of work figuring it out. Maybe I’m not seeing your point.

  • Of course – at the end you have to update the DOM somehow. But if there were a model containing all the ACF data it wouldn’t interfere with anything, you sill would be able to traverse the DOM directly, but you would be able to traverse the model as well. The model should store references to DOM elements.

    The biggest problem about DOM traversing is relying on document structure, which may change (because of changes in ACF or in WP core).

    Another abstraction layer would make the logic not dependent on document structure.

    OK, I made a huge off-topic 🙂

  • @hube2

    A little update. I’ve just found that ACF exposes a global acf JS object. So instead of DOM traversing we’re able to use its fields and methods to get the fields data.

    For example: acf.fields.google_map is an object containing a google_map field type data, a reference to the DOM element, fields settings, etc.

    But it returns only one field of a given type (the last one). I didn’t find yet how to get the data of a specific field of a given type. I’ll try to figure it out and I’ll update this post.

  • I’m not sure if I can help you with what your looking for specifically, but since the last time I commented on this topic I have been doing a bit of testing and working with the ACF JS and learning how to extend it and create new functionality for fields. I don’t know if any of this will help you but you can look at the JS I’ve created for some things in these files https://github.com/Hube2/acf-input-counter/blob/master/acf-input-counter.js https://github.com/Hube2/acf-custom-field-locations-rule/blob/master/acf-custom-field-location-rule.js https://github.com/Hube2/blunt-parametric-search/blob/master/admin/js/admin.js.

    I don’t think that any of it will specifically answer how to get a value from a certain field. ACF itself relies heavily on the data-key attribute of the field wrapper, if that’s any help to you.

  • The main thing is that there’s not a lot of information available for working on the JS side of ACF and I think that the only way that this information and examples will be created is that people like use do the digging, figure it out and post them somewhere for others to see.

  • Hey,

    I’m not looking for help 🙂 I’ve found a solution already (not a clean solution, but at least it works). It was easy. I just wanted to share the knowledge. For many people, it might not be so straightforward because there’s no documentation for ACF JS objects.

    Here’s the another example where I make use of the acf.fields object.

    But thanks for the links anyway. Sometimes creating a new field type that extends the built-in field might be a better idea than trying to “hack” the existing fields.

    PS
    I would appreciate if @eliot commented on this topic. The only thing I wasn’t able to find out is how to get all the instances of the field of a given type.

    As I said in the previous comment: an acf.fields.{FIELD_TYPE} object contains only a one field of the given type. How to get the other fields?

Viewing 10 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic.