1

I have a widget that I have been working on, that contains a <script> tag inside the $widget->form() method. I use the following in my source code, within the script tag:

<script>
    (function ($) {
        var desrdPostTypeSelector = '#<?php echo $this->get_field_id( 'desired_post_type' ); ?>';
        var beginPostTypeSelector = '#<?php echo $this->get_field_id( 'beginning_post_type' ); ?>';

        $('#<?php echo $this->get_field_id( 'isSingle' ); ?>').on( 'change', function() {
            $( '.<?php echo $multi_post_class ?>, #<?php echo $beginning_post_tag_id ?>' ).toggleClass('hidden', this.checked);
        });

        $('#<?php echo $this->get_field_id( 'isTaxonomy' ); ?>').on( 'change', function() {
            $( '#<?php echo $beginning_post_tag_id ?>' ).toggleClass('hidden', this.checked);
        });

        $('#<?php echo $this->get_field_id( 'isSingle' ); ?>, #<?php echo $this->get_field_id( 'isTaxonomy' ); ?>').on( 'change', function () {
            if ( $('#<?php echo $beginning_post_tag_id ?>').hasClass('hidden') ) {
                clearAllDisabledBy( getListOfOptionElements( $( desrdPostTypeSelector )[0] ) );
            } else {
                disableOptionsBasedOnSelected( $( beginPostTypeSelector )[0], $( desrdPostTypeSelector )[0] );
            }
        } );

        $('#<?php echo $this->get_field_id( 'beginning_post_type' ); ?>').on( 'change submit', function () {
            clearAllDisabledBy( getListOfOptionElements( $( desrdPostTypeSelector )[0] ) );
            disableOptionsBasedOnSelected( this, $( desrdPostTypeSelector )[0] );
        } );
    })(jQuery);
</script>

Then, when I go to Appearance -> Widgets and I copy my widget over to a sidebar none of the JavaScript works! I looked at the rendered HTML and saw this:

image showing the unrendered widget number

Why is the unrendered widget number __i__ showing up in the JavaScript but not in the HTML portion? This is a fresh drag-and-drop instance of the widget, mind you, no page refresh. However, when I refresh the page, everything works fine. Am I doing something wrong here?

The file where the JavaScript functions are located is being included in the Sources list in dev tools, I can also see it in the HTML, but for kicks here's how I enqueue that:

In plugin file:

function enqueue_scripts() {
    wp_enqueue_script(
        'related_posts_script_helpers',
        plugin_dir_url( __FILE__ ) . "assets/related-posts-script-helpers.js",
        array( 'jquery' ),
        '1.0'
    );
}

if ( is_admin() ) {
    add_action( 'admin_enqueue_scripts', 'enqueue_scripts' );
}

Is this is a timing issue (on my part) or a bug in WordPress? Any help would be much appreciated. Thank you!

1 Answer 1

2

the __i__ widget instance is a place holder/template for new widget instances when they are create on the UI. Widgets are assigned an actual widget number that will replace the __i__ when they are added to a sidebar and then An ajax request is sent from the UI to the server to update it with the new number, no response is expected from the server for that.

So what happens is that the __i__ instance is just duplicated into the sidebar UI with the only thing being changed is the __i__ being replaced with an actual number on all of the form fields. your JS is not a form field.

Hopefully this explains the why, and why it works after a save (a widget save will probably also solve the problem), as after the save the widget's HTML is fully generated on the server side and not only emulated in JS.

How to fix? do not add JS snippets in the form. Use classes on the elements you want to make "dynamic" and use the jQuery APIs which can locate the element relative to the currently clicked one. For example, if the element is on the "root" of the widget, get the element parent and find() the element below it which has the class you want to manipulate.

3
  • I don't typically make a habit of putting JS snippets in my widget form code. It just seemed like an awful lot of unecessary traversing to do it the way you suggested. There's no other way to re-render the widget? No filter or action or anything to fake a request? I just want to make sure before I put in this extra work. Also, I know that $widget->update() gets called when a new instance is created in the sidebar.
    – mrClean
    Apr 28, 2017 at 13:22
  • 1
    the initialization of a new widget happens fully on JS side, and even if there is some way to hack it, I assume it will be 10x harder to write and maintain than to change your JS as I described Apr 28, 2017 at 13:28
  • That was my thought too --- incurring technical debt --- ah well, lesson learned.
    – mrClean
    Apr 28, 2017 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.