souramoo / commentoplusplus

Commento with out of the box patches and updates to add useful features and fixes. Also with one-click deploy to Heroku so you can get up and running fast.
MIT License
389 stars 62 forks source link

Duplicate Commento divs in Vue SPA #101

Closed Gardamuse closed 2 years ago

Gardamuse commented 2 years ago

I am trying to add Commento++ to a SPA built in Vue. On my site the main page has a list of links to subpages. Each subpage has a commento-box div and runs a function almost identical to that from the README on the component's onMounted hook (runs when the DOM tree has been rendered). By adding two console.log to this function, I have tested that the commento.main() or commento.reInit() is only run once when switching to a new subpage, as expected. The main page does not run any Commento code.

This works, but sometimes, especially in a particular scenario, a subpage will have some of the Commento elements duplicated. It's always the same elements being duplicated, see below.

image

The issue happens a bit inconsistently, but the best way to trigger it (happens maybe 80% of the time) is to:

  1. Go to main page.
  2. Refresh page.
  3. Go to subpage. (Usually no duplication.)
  4. Go to main page (via a link from subpage, so there is no page reload).
  5. Go to subpage. (Duplication happens approx 80% of the time.)

When the issue has occurred once, moving to other subpages or back to the same subpage usually does not trigger the issue again until the page is reloaded while on the main page. (But it has triggered a couple of times in this scenario too, I believe.)

I had the same issue when trying to add the hosted Commento (not ++) widget.

Any suggestions on what might cause this or how it could be resolved would be appreciated!

Gardamuse commented 2 years ago

I probably know too little about Vue and/or Commento to figure out why this happens, but moving the code to its own Vue component seems to mitigate this issue.

So in summary for anyone else wanting to use this with Vue (Vue 3, composition API), I simply created the following component as Commento.vue (remember to replace my.domain.com with your commento domain):

<script setup>
import { onMounted } from "vue";

const props = defineProps({
  pageId: String,
});

function initCommento() {
  let pageId = props.pageId;

  if (typeof window !== "undefined" && !window.commento) {
    // init empty object so commento.js script extends this with global functions
    window.commento = {};
    const script = document.createElement("script");
    // Replace this with the url to your commento instance's commento.js script
    script.src = `https://my.domain.com/commento/js/commento.js`;
    script.defer = true;
    // Set default attributes for first load
    script.setAttribute("data-auto-init", "false");
    script.setAttribute("data-page-id", pageId);
    script.setAttribute("data-id-root", "commento-box");
    script.onload = () => {
      // Tell commento.js to load the widget
      window.commento.main();
    };
    document.getElementsByTagName("head")[0].appendChild(script);
  } else if (typeof window !== "undefined" && window.commento) {
    // In-case the commento.js script has already been loaded reInit the widget with a new pageId
    window.commento.reInit({
      pageId: pageId,
    });
  }
}

onMounted(() => {
  initCommento();
});
</script>

<template>
  <div id="commento-box"></div>
</template>

<style lang="scss" scoped>
</style>

and then use this component as such:

<Commento :page-id="some-id-variable"></Commento>