How to listen to changes in the DOM – An intro to Mutation Observer

Today I faced an issue in one of the developments I work on. We have a form with an input radio and, for some reason, one of the choices was disabled.

I inspected the source html to check if the disabled attribute was being set from the server but it wasn’t. At this point I knew that some JS was causing the issue but, how to determine which script was doing that.

Mutation Observer to the rescue:

The MutationObserver is part of the Web Api of the DOM.

It has very good support fot the majority of browsers, see: canIuse.

The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature, which was part of the DOM3 Events specification.

How to listen to the changes made to an element?

var element = document.querySelector('#myId');
    setTimeout(function() {
        element.setAttribute('data-text', 'whatever');
    }, 5000)

var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.type == "attributes" && mutation.attributeName === 'disabled') {
                console.log("attributes changed", mutation);debugger;

    observer.observe(element, {
        attributes: true //configure it to listen to attribute changes

The snipped above will make the debugger to stop just after the change is made.

Once you are in the debugger you can see the callstack and see the chain of events that triggered the change.

I was able to find the solution.

I hope this helps someone else.

Wordpress Code

How to setup a wordpress multisite with docker compose and xdebug in minutes

How to set up a wp site fast with xdebug.

This is the end result:


  • Docker


Create a Dockerfile like this one:

FROM wordpress:latest


RUN pecl install "xdebug" \
    && docker-php-ext-enable xdebug

RUN echo "xdebug.remote_enable=1" >> /usr/local/etc/php/conf.d/xdebug.ini && \
    echo "xdebug.remote_autostart=1" >> /usr/local/etc/php/conf.d/xdebug.ini && \
    echo "xdebug.remote_port=${XDEBUG_PORT}" >> /usr/local/etc/php/conf.d/xdebug.ini && \
    echo "xdebug.idekey=${XDEBUG_IDEKEY}" >> /usr/local/etc/php/conf.d/xdebug.ini

Create docker-compose.yml file like this one:

version: '3.1'


    build: .
      - 80:80
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
        remote_host= # also could be host.docker.internal
      PHP_IDE_CONFIG: serverName=php-docker

      - ./wp:/var/www/html
      - ./templates/.htaccess:/var/www/html/.htaccess

    image: mysql:5.7
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass

    image: wordpress:cli
    user: '33'
      HOME: /tmp
      - ./wp:/var/www/html

    image: adminer
      - 9999:8080

Create an .htaccess file like this one:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]

Now run the followin commands:

# create a templates directory
mkdir templates

# create a wp directory
mkdir wp

# move .htaccess file to template dir
mv .htaccess templates

# start docker containers
docker-compose up -d

# wait 5 seconds
# install wordpress multisite
docker-compose run --rm cli core multisite-install \
  --url=http://localhost \
  --title="test site" \
  --admin_user="admin" \
  --admin_password="admin" \
  --admin_email="" \

We will end with an structure like:

Now visit http://localhost


Wordpress Code

A tale about polyfills

What is babel polyfill?

babel-polyfill is a lib that you can import into your code. By doing so it will emulate a full ES6 environment.

The following code will not run in any browser without the necessary fixes that babel-polyfill provides.

    var nodes = document.querySelectorAll('.my-wanted-elements');
    // here we have a NodeList, but we want an Array of elements.
    var els = Array.from(nodes);
    // els is an array

Ok, so can I use it in every script in my plugins?

NO, you shouldnt use it in that way.

In wordpress there is a polyfill provided by the wp-polyfill.

It is bad practice to include the babel-polyfill several times.

Let’s imagine the following scenario

We have Plugin A and Plugin B, both enqueue a JS file via wp_enqueue_script().

If we use Gulp or Webpack to build our assets we may be tented to inject the babel-polyfill in both scripts.


Ok, so what is the proposed solution?

WordPress official documentation states the following:

It is recommended to use the main wp-polyfill script handle which takes care of loading all the below mentioned polyfills.

It also says:

When using a JavaScript bundler like webpack, the scripts mentioned here can be excluded from the bundle and provided by WordPress in the form of script dependencies.



provides a webpack plugin to help extract WordPress dependencies from bundles. @wordpress/scripts

build script includes

the plugin by default.

So you can use a webpack setup and add the plugin mentioned above and it will remove the dependencies from wordpress.

This way you let WP inject the polyfills independently from your scripts. You will still need to add wp-polyfill as dependency.

Another solution can be to write in ES6 and use wp-polifyll as dependencies.

Wordpress Code

A tip on wordpress add_rewrite_rule()

When you use add_rewrite_rule() function keep in mind the following:

  • Use it inside some hook, normally inside init hook.
  • It wont work unless you have custom permalinks activated.
  • Flush your permalinks

Show me some snippet

// register a custom parameter
add_filter( 'query_vars', function( $query_vars ) {
     $query_vars[] = 'myparamname';
     return $query_vars;
}, 30, 1 );

// do some action based on the custom param
add_action( 'template_include', function ($template) {

    if (get_query_var('myparamname') === 'someval') {
        return 'some other template';   
    return $template;
}, 10, 1 );

// apply the rule
add_action( 'init',  function() {

    add_rewrite_rule( 'patata', 'index.php?myparamname=someval', 'top' );

}, 1, 0 );