Why does my custom plugin only function correctly once per page?

I have a custom plugin that uses shortcodes to generate tables which are meant to be manipulated by the user. As they change the number of servings in A2, it will divide the values in B2-E2 by the value in A2. Why does my custom plugin only function correctly once per page?

The shortcode and table functions correctly the first time it appears on the page. If I try to use the shortcode again, it will generate the table but nothing happens when the value in cell A2 is changed, it doesn’t divide the values like it’s supposed to.

Any ideas?

Here is my .js code:

    'use strict';

    var updateNutritionValues = function updateNutritionValues(multipler, macros) {
      var carbs = document.querySelector('.nutrition-table__content--carbs');
      var protein = document.querySelector('.nutrition-table__content--protein');
      var fat = document.querySelector('.nutrition-table__content--fat');
      var calories = document.querySelector('.nutrition-table__content--calories');

      carbs.textContent = (macros.carbs / parseInt(multipler)).toFixed(1) + 'g';
      protein.textContent = (macros.protein / parseInt(multipler)).toFixed(1) + 'g';
      fat.textContent = (macros.fat / parseInt(multipler)).toFixed(1) + 'g';
      calories.textContent = (macros.calories / parseInt(multipler)).toFixed(0) + ' cals';
      console.log('carbs => ', (macros.carbs / parseInt(multipler)).toFixed(1) + 'g');
      console.log('carbs => ', (macros.protein / parseInt(multipler)).toFixed(1) + 'g');
      console.log('carbs => ', (macros.fat / parseInt(multipler)).toFixed(1) + 'g');
      console.log('carbs => ', (macros.calories / parseInt(multipler)) + ' cals');
    };

    var nutritionTable = function nutritionTable() {
      var number = document.querySelector('.nutrition-table__number');
      var originalValues = {
        carbs: parseInt(document.querySelector('.nutrition-table__content--carbs').textContent),
        protein: parseInt(document.querySelector('.nutrition-table__content--protein').textContent),
        fat: parseInt(document.querySelector('.nutrition-table__content--fat').textContent),
        calories: parseInt(document.querySelector('.nutrition-table__content--calories').textContent)
      };

      number.addEventListener('change', function (e) {
        updateNutritionValues(parseInt(e.target.value), originalValues);
      });
    };

    document.addEventListener('DOMContentLoaded', function () {
      return nutritionTable();
    });

Here is my .php code

    function ls_nutritional_table_resources() {
        global $post;
        if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'nutrition_table') ) {
            wp_register_style( 'ls-nutritional-table-css',  plugin_dir_url( __FILE__ ) . 'nutritional-table.css' );
            wp_enqueue_style( 'ls-nutritional-table-css' );

            wp_enqueue_script('ls-nutritional-table-js', plugin_dir_url( __FILE__ ) . 'nutritional-table.js' );
        }
    }

    add_action( 'wp_enqueue_scripts', 'ls_nutritional_table_resources');


    add_shortcode('nutrition_table', 'ls_nutritional_table');

    function ls_shortcodes() {
        function ls_nutritional_table($atts) {
            $attributes = shortcode_atts(array(
                'servings' => '1',
                'carbs' => '100',
                'protein' => '400',
                'fats' => '90',
                'calories'=> '2810'
            ), $atts);

            $output = '<div class="nutrition-table">
            <div class="nutrition-table__column">
            <div class="nutrition-table__head">Servings</div>
            <div class="nutrition-table__content">
                <input type="number" value="' . esc_attr($attributes['servings']) .  '" min="1" autocomplete="off" class="nutrition-table__number">
            </div>
            </div>
            <div class="nutrition-table__column">
            <div class="nutrition-table__head">Carbs</div>
            <div class="nutrition-table__content nutrition-table__content--carbs">' . esc_attr($attributes['carbs']) .  'g</div>
            </div>
            <div class="nutrition-table__column">
            <div class="nutrition-table__head">Protein</div>
            <div class="nutrition-table__content nutrition-table__content--protein">' . esc_attr($attributes['protein']) .  'g</div>
            </div>
            <div class="nutrition-table__column">
            <div class="nutrition-table__head">Fat</div>
            <div class="nutrition-table__content nutrition-table__content--fat">' . esc_attr($attributes['fats']) .  'g</div>
            </div>
            <div class="nutrition-table__column">
            <div class="nutrition-table__head">Calories</div>
            <div class="nutrition-table__content nutrition-table__content--calories">' . esc_attr($attributes['calories']) .  ' cals</div>
            </div>
        </div>';

      return $output;
        }
    }

    add_action('init', 'ls_shortcodes');

    /** Always end your PHP files with this closing tag */
    ?>

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The reason your code only applies to the first table, is because you are only selecting the first input with var number = document.querySelector('.nutrition-table__number');

You need to loop over all your tables, and add an event listener to each.

Here is an example of how this could work. The plugin pattern is from https://vanillajstoolkit.com/boilerplates/revealing-module-pattern/

/*!
 * Revealing Constructor Pattern Boilerplate
 * (c) 2019 Chris Ferdinandi, MIT License, https://gomakethings.com
 */
var NutritionTablesPlugin = (function () {

    'use strict';

    /**
     * Create the Constructor object
     */
    var Constructor = function (selector) {

        //
        // Variables
        //

        var publicAPIs = {};

        var nodes = document.querySelectorAll(selector);

        //
        // Methods
        //
        var allNodes = function (callback) {
            for (var i = 0; i < nodes.length; i++) {
                callback(nodes[i], i);
            }
        };

        /**
         * A private method
         */
        var updateNutritionValues = function (table, multipler, macros) {
            var carbs = table.querySelector( '.nutrition-table__content--carbs');
            var protein = table.querySelector('.nutrition-table__content--protein');
            var fat = table.querySelector('.nutrition-table__content--fat');
            var calories = table.querySelector('.nutrition-table__content--calories');

            carbs.textContent = (macros.carbs / parseInt(multipler)).toFixed(1) + 'g';
            protein.textContent = (macros.protein / parseInt(multipler)).toFixed(1) + 'g';
            fat.textContent = (macros.fat / parseInt(multipler)).toFixed(1) + 'g';
            calories.textContent = (macros.calories / parseInt(multipler)).toFixed(0) + ' cals';
        };


        /**
         * Another public method
         */
        publicAPIs.init = function (options) {
            allNodes( function(node){

                var originalValues = {
                    carbs: parseInt(node.querySelector('.nutrition-table__content--carbs').textContent),
                    protein: parseInt(node.querySelector('.nutrition-table__content--protein').textContent),
                    fat: parseInt(node.querySelector('.nutrition-table__content--fat').textContent),
                    calories: parseInt(node.querySelector( '.nutrition-table__content--calories').textContent)
                };

                node.addEventListener('change', function (e) {
                    updateNutritionValues(node, parseInt(e.target.value), originalValues);
                });
            });
        };

        //
        // Return the Public APIs
        //
        return publicAPIs;
    };

    //
    // Return the Constructor
    //
    return Constructor;

})();

document.addEventListener("DOMContentLoaded", function(event) {
    var nutrition = new NutritionTablesPlugin('.nutrition-table');
    nutrition.init();

});

And in your PHP code, you’ll want to use esc_html for the carbs/fat/protein/calories attributes output. https://developer.wordpress.org/reference/functions/esc_html/


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x