How to make links clickable in a chat

2019-02-20 13:22发布

I have a chat on my website that reads from a JSON file and grabs each message and then displays it using Vue.js. However, my problem is that when a user posts a link, it is not contained in an anchor tag <a href=""/>. Therefore it is not clickable.

I saw this post, and I think something like this would work, however, I am not allowed to add any more dependencies to the site. Would there be a way for me to do something similar to this without adding more dependencies?

Code for displaying the message.

<p v-for="msg in messages">
    <em class="plebe">
        <b>&nbsp;[ {{msg.platform.toUpperCase()}} ]&nbsp;
            <span style="color: red" v-if="msg.isadmin">{{msg.user.toUpperCase()}}</span>
            <span style="color: #afd6f8" v-else="">{{msg.user.toUpperCase()}}</span>
        </b>
    </em>:&nbsp;
    {{msg.message}}
</p>

标签: html json vue.js
2条回答
Deceive 欺骗
2楼-- · 2019-02-20 13:52

In a situation like this, its preferred to write a custom functional component.

The reason for this is the fact that we are required to emit a complex html structure, but we have to make sure to properly protect against xss attacks (so v-html + http regex is out of the picture)

We are also going to use render functions, because render functions have the advantage to allow for javascript that generates the html, having more freedom.

<!-- chatLine.vue -->
<script>
    export default {
        functional: true,
        render: function (createElement, context) {
            // ...
        },
        props: {
            line: {
                type: String,
                required: true,
            },
        },
    };
</script>
<style>
</style>

We now need to think about how to parse the actual chat message, for this purpose, I'm going to use a regex that splits on any length of whitespace (requiring our chat urls to be surrounded with spaces, or that they are at the start or end of line).

I'm now going to make the code in the following way:

  1. Make a list for child componenets
  2. Use a regex to find url's inside the target string
  3. For every url found, do:
    1. If the match isn't at the start, place the text leading from the previous match/start inside the children
    2. place the url inside the list of children as an <a> tag, with the proper href attribute
  4. At the end, if we still have characters left, at them to the list of children too
  5. return our list wrapped inside a P element

Vue.component('chat-line', {
  functional: true,
  // To compensate for the lack of an instance,
  // we are now provided a 2nd context argument.
  // https://vuejs.org/v2/guide/render-function.html#createElement-Arguments
  render: function (createElement, context) {
    const children = [];
    let lastMatchEnd = 0;
    // Todo, maybe use a better url regex, this one is made up from my head
    const urlRegex = /https?:\/\/([a-zA-Z0-9.-]+(?:\/[a-zA-Z0-9.%:_()+=-]*)*(?:\?[a-zA-Z0-9.%:_+&/()=-]*)?(?:#[a-zA-Z0-9.%:()_+=-]*)?)/g;
    const line = context.props.line;
    let match;
    while(match = urlRegex.exec(line)) {
      if(match.index - lastMatchEnd > 0) {
        children.push(line.substring(lastMatchEnd, match.index));
      }
      children.push(createElement('a', {
        attrs:{
          href: match[0],
        }
      }, match[1])); // Using capture group 1 instead of 0 to demonstrate that we can alter the text
      lastMatchEnd = urlRegex.lastIndex;
    }
    if(lastMatchEnd < line.length) {
      // line.length - lastMatchEnd
      children.push(line.substring(lastMatchEnd, line.length));
    }
    return createElement('p', {class: 'chat-line'}, children)
  },
  // Props are optional
  props: {
    line: {
      required: true,
      type: String,
    },
  },
});

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello <script>, visit me at http://stackoverflow.com! Also see http://example.com/?celebrate=true'
  },
});
.chat-line {
  /* Support enters in our demo, propably not needed in production */
  white-space: pre;
}
<script src="https://unpkg.com/vue@2.0.1/dist/vue.js"></script>
<div id="app">
  <p>Message:</p>
  <textarea v-model="message" style="display: block; min-width: 100%;"></textarea>
  
  <p>Output:</p>
  <chat-line :line="message"></chat-line>
</div>

查看更多
孤傲高冷的网名
3楼-- · 2019-02-20 14:08

You can watch or write computed method for the variable having url and manupulate it to html content and then use v-html to show html content on the page

v-html

查看更多
登录 后发表回答