What is the best way to add an element conditionally within a specific Vue Component scope?

I've been working on creating a Component for titles that are editable when double-clicked. The Component takes the specific h-tag and title as props, generating a regular h-tag that transforms into an input field upon double click. It's functioning properly with single instances on a page, but encounters issues when multiple Components are utilized due to improper scoping. I'm currently stuck on how to resolve this issue, here is the code snippet:

<template>
  <div class="edit-on-click">
    <input
      :class="sizeClass"
      type="text"
      v-if="edit"
      v-model="editedTitle"
      @blur="finishEdit"
      @keyup.enter="finishEdit"
      v-focus="true"
    />
    <span v-show="!edit" @dblclick.prevent="edit = true"></span>
  </div>
</template>

Struggling with scoping in the mounted hook:

  mounted() {
    let node = document.createElement(this.size); // Accepts h-tag (h1, h2 etc.)
    let titleText = document.createTextNode(this.finalTitle); // Accepts title

    node.appendChild(titleText);
    node.classList.add("editable-title");

    // This causes issues with multiple components on the page
    document.getElementsByTagName("span")[0].appendChild(node);
  },

Any suggestions on how to efficiently scope this? Thanks in advance!

Answer №1

When using Vue, it is recommended to avoid creating DOM elements in the traditional way as much as possible to prevent race conditions where Vue is not aware of these elements that you may want to be reactive at some point (such as the <span> double-clicking in your case).

Instead, consider dynamically switching between different headings using the <component> element and the v-bind:is property. Here is an example:

Vue.component('EditableHeading', {
  template: '#editable-heading',

  props: {
    size: {
      type: String,
      default: 'h1'
    },
    value: {
      type: String,
      required: true
    }
  },

  data() {
    return {
      editing: false
    }
  },

  methods: {
    confirm(e) {
      this.$emit('input', e.target.value);
      this.close();
    },
    start() {
      this.editing = true;
      
      this.$nextTick(() => {
        this.$el.querySelector('input[type="text"]').select();
      });
    },
    close() {
      this.editing = false;
    }
  }
})

new Vue({
  el: '#app',

  data: () => ({
    titleList: [],
    text: 'New Title',
    size: 'h3'
  }),

  methods: {
    addNewTitle() {
      this.titleList.push({
        text: this.text,
        size: this.size
      });
    }
  }
})
.edit-on-click {
  user-select: none;
}

.heading-size {
  margin-top: 1rem;
  width: 24px;
}

p.info {
  background-color: beige;
  border: 1px solid orange;
  color: brown;
  padding: 4px 5px;
  margin-top: 2rem;
}
<script src="https://vuejs.org/js/vue.min.js"></script>

<div id="app">
  <editable-heading 
    v-for="(title, index) of titleList" :key="index" 
    v-model="title.text" 
    :size="title.size">
  </editable-heading>

  <div>
    <label>
      Heading size: 
      <input v-model="size" class="heading-size" />
    </label>
  </div>
  <div>
    <label>
      Title: 
      <input v-model="text" />
    </label>
  </div>
  <div>
    <button @click="addNewTitle()">Add new title</button>
  </div>

  <p class="info">
    [double-click]: Edit <br />
    [enter]: Confirm <br />
    [esc/mouseleave]: Cancel
  </p>
</div>

<script id="editable-heading" type="text/x-template">
  <div class="edit-on-click">
    <input 
      type="text" 
      v-if="editing" 
      :value="value" 
      @blur="close" 
      @keydown.enter="confirm" 
      @keydown.esc="close" />

    <component :is="size" v-else @dblclick="start">{{value}}</component>
  </div>
</script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to pass a file and a string to my C# backend using JQuery Ajax?

I have a user interface with two input fields - one for uploading a file and another for entering the file's name. I need to capture both the file (as binary data) and its corresponding name in a single AJAX request, so that I can upload the file usi ...

Undefined method error encountered within Vue.js nested ref structure

My component structure is three levels deep and it's set up like this: - container - section-1 // section1Ref - form-1 // form1Ref The submit method in the container component will call the submit method in section-1 using this.$refs.section1R ...

"Utilizing Google Tag Manager to trigger events and push them to the data layer

My goal with this code is to trigger an event in the data layer through Google Tag Manager whenever a user hovers over a specific area on the website for at least 1 second. The challenge I'm facing is that I have 8 other areas on the site using the sa ...

I am looking to view all products that belong to the category with the ID specified in the request, and have red-colored stocks

Within my database, I have defined three mongoose models: Product, Category, and Stock. The Product model contains two arrays - categories and stocks, each including respective category and stock ids. My goal is to retrieve all products where the category_ ...

React UseEffect - Triggering only when data has been updated

In my current situation, I am facing a dilemma with the useEffect hook. I need it to not run on the initial render, but only when specific data is rendered, such as home.matchsPlayed and home.winHome. This is what my code looks like: useEffect(() => ...

Encountered an error: Unable to access property '0' of an undefined variable

Below is the script I am using: <script> var count; var obj; function search() { xhr = new XMLHttpRequest(); xhr.open("GET", "test.json", true); xhr.send(null); xhr.onreadystatechange = function() { if (xhr.readyState == 4 & ...

Capture line breaks from textarea in a JavaScript variable with the use of PHP

I need help with handling line breaks in text content from a textarea. Currently, I am using PHP to assign the textarea content to a Javascript variable like this: var textareaContent = '<?php echo trim( $_POST['textarea'] ) ?>'; ...

Embrace the presence of null values on the client side

When utilizing the code below, I can determine the location of logged-in users. However, there are some users who do not have a specific location assigned. For example, Administrators are common for all locations. In such cases, how can I set it so that ...

How can I pass the data-attribute ID from JavaScript to PHP on the same index page using ajax?

I am struggling with the title for this section. Please feel free to modify it as needed. Introduction: I have been working on setting up a datatables.net library server-side table using JSON and PHP. Most of the work is done, but I am facing challenges w ...

Utilizing Navigate and useState for Conditional Routing in React

Looking for assistance with a React app. Here's the code snippet: function App() { const [walletConnected, setWalletConnected] = useState("") async function checkConnection() { const accounts = await window.ethereum.request({ method: 'e ...

Struggling with setting values in AngularJS?

Can you assist me in passing data from: (Page 1 -> Module 1 -> Controller 1) to (Page 2 -> Module 2 -> Controller 2) For example: Module reports - Controller ReportsCtrl //Page 1 <html lang="en" class="no-js" ng-app="reports"> <a n ...

When you drag down on mobile Safari on an iPad, touch events may cease to fire in HTML5

When I implement event listeners to handle touch events like touchmove and touchstart document.addEventListener("touchstart", function(event){ event.preventDefault(); document.getElementById("fpsCounter").innerHTML = "Touch ...

The process of loading and saving extensive data to the local storage of a user's Firefox browser involves extensive reading and writing operations

Our team has developed a sophisticated HTML5 offline application that houses a substantial amount of data, totaling tens of megabytes. We are looking for a way to allow users to save and retrieve copies of this data on their disk. While we have made some ...

Identification of input change on any input or select field within the current modal using JavaScript

My modal contains approximately 20 input and select fields that need to be filled out by the user. I want to implement a JavaScript function to quickly check if each field is empty when the user navigates away or makes changes. However, I don't want t ...

What is the process of installing an npm module from a local directory?

I recently downloaded a package from Github at the following link: list.fuzzysearch.js. After unzipping it to a folder, I proceeded to install it in my project directory using the command: npm install Path/to/LocalFolder/list.fuzzysearch.js-master -S Howe ...

Showing the information obtained from an API request once the user submits the form without the need to reload the page

I am currently working on a form that will take search query requests from users upon submission and then display the results by making an API call. My goal is to show these results without the page having to refresh, using AJAX. The backend connection to ...

Introducing a fresh parameter to initiate and end Server Sent Events

Assistance needed. I have implemented Server Sent Events to dynamically update a website using data from a database. Now, I aim to send a new parameter ('abc.php/?lastID=xxx') back to the PHP script based on information received in the previous ...

JavaScript JCrop feature that allows users to resize images without cropping

I'm currently attempting to utilize JCrop for image cropping, but I'm running into frustratingly incorrect results without understanding why. The process involves an image uploader where selecting an image triggers a JavaScript function that upda ...

"Using Jest to specifically test the functionality of returning strings within an object

Attempting to run a jest test, it seemed like the issue was with the expect, toBe section as I believed that the two objects being compared (data, geonames) were exactly the same. However, upon closer inspection, they turned out to be different. The struct ...

What is the best way to achieve the functionality of this ajax jquery using vanilla JavaScript?

I am attempting to replicate this jQuery ajax POST request in Vanilla JS, which currently looks like: $.ajax({ method: 'POST', url: window.location.href + 'email', data: { toEmail: to, fromName: from, ...