In Vue, it is not accurate to measure overflow

I am working on creating an overflow effect with tagging that fades out at the beginning to provide a subtle hint to users that there is more content. Here is what it currently looks like:

https://i.stack.imgur.com/fXGBR.png

To achieve this, I added a fading gradient as a :after element in the CSS and "activated" it using Vue's style binding when scrollWidth > offsetWidth (indicating overflow bigger than the box itself).

However, I have encountered a problem where the calculation sometimes lags and does not accurately determine the scrollWidth, especially when entering a long word and then deleting it. In such cases, even though there is no tag remaining in the box, the overflow status is still falsely indicated. This behavior can be seen here: https://i.stack.imgur.com/yVhhd.png

I attempted to address this issue by placing the calculation inside a $nextTick(), but it did not resolve the problem. Additionally, I tried implementing Vue's keyDown, keyUp, and keyPress listeners, all to no avail.

A demonstration of this issue can also be viewed on CodePen.

Here is the code snippet showcasing the problem:

new Vue({
  el: '#tagsinput',
  data: {
    input_value: "",
    tags: []
  },
  methods: {
    addTag: function() {
      if (this.input_value > "") {
        this.tags.push(this.input_value)
        this.input_value = "";

        // Refocus the text input, so it stays at the end
        this.$refs.input.blur();
        this.$nextTick(function() {
          this.$refs.input.focus();
        })

      }
    },
    deleteTag: function() {
      if (this.input_value == "") {
        this.tags.pop()
      }
    }
  }
})
.outter {
  border: 1px solid red;
  width: 250px;
  overflow: hidden;
  display: flex;
}

.inner {
  border: 1px solid blue;
  margin: 2px;
  display: flex;
}

.tag {
  border: 1px solid black;
  margin: 2px;
}

input {
  min-width: 80px;
  width: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.2/vue.min.js"></script>
<div id="tagsinput">
  <div class="outter" ref="outter">
    <div class="inner" ref="inner">
      <div class="tag" v-for="tag in tags">{{tag}}</div><input type="text" v-model="input_value" @keydown.enter="addTag" @keydown.delete="deleteTag">
    </div>
  </div>
  Outter div scrollwidth: {{ $refs.outter ? $refs.outter.scrollWidth : null }}<br> Outter div offsetWidth: {{ $refs.outter ? $refs.outter.offsetWidth : null }}<br>
  <br> Is overflowing: {{ ($refs.outter ? $refs.outter.scrollWidth : null) > ($refs.outter ?$refs.outter.offsetWidth : null) }}
</div>
<br><br> Type a really long word in, add and then delete it. "Is overflowing" will be the inverse, until you press Backspace <b>again</b>.

Any assistance with resolving this issue would be highly appreciated.

Answer №1

Make sure to check for overflow after adding or deleting a tag to ensure it happens at the right time. Vue does not bind inline conditions like that. Use the code below, which calls a function checkOverflow within $nextTick to set a data-bound variable isOverflowed for styling purposes.

new Vue({
    el: '#tagsinput',
    data: {
        input_value: null,
        tags: [],
        isOverflowed: false
    },
    methods: {
        addTag: function() {
            if(this.input_value) {
                this.tags.push(this.input_value)
                this.input_value = null;

                // Keep text input focused at the end
                this.$refs.input.blur();
                this.$nextTick(function() {
                    this.$refs.input.focus();
                    this.checkOverflow()
                })
            }
        },
        deleteTag: function() {
            if(!this.input_value) {
                this.tags.pop()
                this.$nextTick(function() {
                    this.checkOverflow()
                })
            }
        },
        checkOverflow: function() {
            this.isOverflowed = (this.$refs.outter ? this.$refs.outter.scrollWidth : null) > 
                (this.$refs.outter ? this.$refs.outter.offsetWidth : null)
        }
    }
})
.outter {
    border: 1px solid red;
    width: 250px;
    overflow: hidden;
    display: flex;
}

.inner {
    border: 1px solid blue;
    margin: 2px;
    display: flex;
}

.tag {
    border: 1px solid black;
    margin: 2px;
}

input {
    min-width: 80px;
    width: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="tagsinput">
    <div class="outter" ref="outter">
        <div class="inner" ref="inner">
            <div class="tag" v-for="tag in tags">{{tag}}</div>
            <input type="text" v-model="input_value" @keydown.enter="addTag" @keydown.delete="deleteTag" ref="input">
        </div>
    </div>
    <br>
    Is overflowing: 
    {{ isOverflowed }}
</div>
<br><br>
Enter a long word, add it, then delete it. "Is overflowing" will change, reverse when you press Backspace <b>again</b>.

Answer №2

Here's a clever trick using CSS and HTML...

To create a fade effect, insert

<div id="spaceFade"></div>
right after <div id="tagsinput">. Then, add this CSS code:

#spaceFade {
  background-image: linear-gradient(to right, rgba(255,255,255,1), rgba(255,255,255,1), rgba(0,0,0,0));
  position: absolute;
  height: 2em;
  width: 3em;
}
#tagsinput {
  position: relative;
}
.outer {
  justify-content: flex-end;
}

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

Tips on sending error messages to an MVC view during an Ajax call

When using ajax to call a controller action method on an MVC button click event, there may be validation logic in the controller that needs to notify the user of any errors. Is there a way to send an error message to the ajax success event from the control ...

Incorporating JSTree Into Your Website's Heading: A Step-By-Step

I'm in search of a way to integrate the following code snippet as a JSTree into the heading section of a webpage, depicted below: <div id="jstree"> <ul> <li>Core 1 <ul> & ...

Access external variables in next.js outside of its environment configuration

Currently, I am developing my application using next js as the framework. While my environment variables work smoothly within the context of next js, I am facing a challenge when it comes to utilizing them outside of this scope. An option is to use dotenv ...

Having trouble launching Cypress on my Mac - stating that it cannot find Cypress

Despite searching through multiple answers on S.O, none of them have solved my issue. To better explain my question, I've created a video. You can view it here Everything was working perfectly just yesterday, so what could have possibly gone wrong? ...

Incorporate React into current Node express application by utilizing a content delivery network (CD

I'm currently in the process of developing a web application utilizing both Node and React. Instead of having separate Node and React applications, I decided to integrate React directly into my existing setup. To achieve this, I attempted importing th ...

Tips for effective page management

After creating a navbar in my project, I've come to the realization that it requires a component for each page and subpage. This seems redundant especially when dealing with multiple navigation options like shown in this image. Is it necessary to crea ...

The getJSON function yields a null value

When using the jQuery getJSON function, I am getting a null response even though everything seems to be written correctly: $.getJSON("/site/ajax/autocomplete/key", function(data) { alert(data); //null alert(data.term); //null }); I am working wit ...

Enhance your user interface by adding a tooltip above a button

I am currently working on creating a button that can copy content from a variable into the clipboard using TypeScript and Material-UI. Here is what I have tried: const [copySuccess, setCopySuccess] = useState(''); const copyToClipBoard = async ( ...

The output of JSTree's data.rslt.obj.text() function is an array of text values, not just the text from the specified node

I am facing an issue with the jstree where it is returning a concatenated list of node names when I try to rename a node, instead of just the text for the node I am renaming. The jstree is set up to load on demand. How can I ensure that only the text for ...

Creating an object positioned to the right side of a div (div:right) is a matter of using CSS positioning properties

While we are familiar with pseudo-classes like :before and :after, have you ever wondered why there is no nav ul li a:left or :right? Do you think it's achievable? I'm open to using HTML5, CSS3, and JavaScript to make it happen. ...

Personalize Badge Component

I've been on the hunt for a solution to customize a badge component similar to what's seen here: https://mui.com/material-ui/react-badge/. As of now, only options for making it a dot or adding a number in a circle are available. However, I' ...

The save button click handler is not triggering JQuery Validation

I've been attempting for hours to get the validation working, but without success. I have added the name attribute to the input element and included script sources as well. Even after firing the validate method, the contents are still not being valida ...

Regex pattern to replace the zero preceding two times within a string based on distinct criteria

I need to transform the string XY4PQ43 using regex in JavaScript. The output should be XY04PQ0043. Specifically, I want to add a zero prefix to the first number if it is a single digit to ensure it has 2 digits, and for the second number in the string, I w ...

Is it possible to customize the border spacing for individual cells?

My table resembles the one shown in this example I am looking to eliminate the gap between each "Previous" and "Current" cell pairs while still maintaining spacing between rows and other columns. Is there a way to achieve this? ...

What is the reason for Cloud Firestore's realtime database fetching all items?

Currently, I am developing a project with vuejs and I am facing an issue where I need to fetch only the last created item from Firestore's realtime database. However, when I execute the following code snippet, it retrieves all items from the database ...

Add a new component to a Vue.js instance that is already in use

Just getting started with Vue.js I'm looking to register a local component following the instructions here: https://v2.vuejs.org/v2/guide/components.html#Local-Registration The catch is that I need to register the component to an existing Vue insta ...

What is the best way to utilize the .done() callback in order to execute a function when new data is loaded upon request?

I have a web page that pulls data from a JSON feed, and there's also a button to load more content from the feed when clicked. I want to add additional elements inside the page for each item in the feed. I've managed to create a function that doe ...

Slideshow of table rows in HTML

On a webpage, I am populating an HTML table with a random number of rows ranging from 1 to 100. Regardless of the total number of rows, the requirement is to display only 10 rows at a time on the screen and then shift to the next set of 10 rows every 5 sec ...

Having trouble retrieving directive parameters in Vue.js?

Vue.directive('customselect', { params: ['selectedTask'], bind: function () { var that = this; $(this.el) .select2() .on('change', function () { that.set(this.value); if (!this.name.matc ...

Enable divs to be interactively chosen

I have created two selectable divs that function like buttons. By using the left and right arrow keys, I am able to select one of the divs with this code: document.addEventListener("keydown", keyDownDocument, false); function keyDownDocument(e) { var k ...