-1

Let's say I have my project done using Vue.JS. I have a lot of HTML using components of Vue.JS and so on... My forms, inputs, buttons... lots of things have their title attribute... Now I wish to make those titles look a bit more fancy. In jQuery it is pretty easy to make a small lib (and there are tons of them) to do this without touching existing html. How would it be done "the vue way"?

[EDIT - more examples]

  1. Imagine I wish to add github.com/Akryum/v-tooltip directive, but don't wish to refactor all my code and include v-tooltip attribute to all my html (call it lazyness). So I'd like to do something like $('a[title]').each(function(){ $(this).attr('v-title', $(this).attr('title')); }); to automatically apply v-title directive to all my links with title attribute.... I understand I may do this BEFORE initializing Vue.js using jQuery or vanilla js but that's kind of silly.
  2. Imagine I use Vuetify and for debugging purpose I wish all my v-btn components to put something to console.log when clicked. In jQuery it is as easy as $('.mybutton').click(function(){ console.log($(this).data() ); });
  3. I wish to show in title/tooltip current value of each input. But again - without need to refactor all my code because maybe I just need it as a temporary measure or A/B test of user experience and it will be removed next day/week... as some pseudocode this would look like $('input').prop(':title', 'The value is {{model.value}}');
dmikam
  • 992
  • 1
  • 18
  • 27
  • 1
    identify a class for that tag and have your own styles as needed. – Sowmyadhar Gourishetty Aug 31 '20 at 10:59
  • 1
    @SowmyadharGourishetty well, by "more fancy" I ment have something like https://api.jqueryui.com/tooltip/ where you can setup position, close button, close on click outside, well a lot of functionality added to simple "title" tooltip... Also as soon as I know, you are very limited in (just for not to say there is no way of) styling of your title tooltips by CSS.. – dmikam Sep 01 '20 at 08:02
  • Also the idea is not to create a "fancy title", but to get the way of doing things like this by "vue way"... – dmikam Sep 01 '20 at 08:14

8 Answers8

2

In vuejs you have a feature called Directive which is a great tool for this type of situation. Just create a global directive ( because it seems that you will be using it in multiple places ) and in it add all your necessary styling and behavior you are interested in.

tony19
  • 125,647
  • 18
  • 229
  • 307
Mohit
  • 1,225
  • 11
  • 28
1

Vue Custom Directives can be a best solution for you in tackling this issue.Basically Directives are tiny commands that you can attach to DOM elements.They are typically useful if you need low-level access to an HTML element to control a bit of behavior. Add whatever styling or behavior you want in this directive and use wherever you want it.

Steps for Creating Custom Directives.

For example you want to make a directive for a background, let say name it CustomBackgroundDirective then first create a new file called CustomBackgroundDirective.js like.

import Vue from 'vue';

const defaultBackgroundColor = '#86bbff'

// Initialize the custom-background directive.
export const CustomBackground = {
  bind(el, binding, vnode) {
    // Allow users to customise the color by passing an expression.
    const color = binding.expression || defaultBackgroundColor

    // el might not be present for server-side rendering.
    if (el) {
      // Set the element's background color.
      el.style.backgroundColor = color
    }
  }
}

// You can also make it available globally.
Vue.directive('custom-background', CustomBackground)


Now to use it in a component, simply add it to your component template prefixed with a v- like

<template>
  <div>
    <p v-custom-background>Baby blue looks good on me.</p>
    <p v-custom-background="#0f0">I prefer neon green.</p>
  </div>
</template>

<script>
import { CustomBackground } from './CustomBackgroundDirective.js';

export default {
  directives: {
    CustomBackground
  }
}
</script>


Moreover there are a few hooks available to the directive with which you can play, and each one has the option of a few arguments. The hooks are as follows:

  • bind – This occurs once the directive is attached to the element.
  • inserted – This hook occurs once the element is inserted into the parent DOM.
  • update – This hook is called when the element updates, but children haven’t been updated yet.
  • componentUpdated – This hook is called once the component and the children have been updated.
  • unbind – This hook is called once the directive is removed.
Saddam
  • 1,174
  • 7
  • 14
  • Do not exactly fit requirements of my question, because in the case of Directives you *need to add new attribute* to every single element you wish to be affected by Directive's funcionality. And I was looking for something _without touching their HTML code_. Like in jQuery you may just `$('a[title]').each(function(){ ... add styling, new nodes, etc... })` But if there is no better answer for a while - this looks like the best candidate to be accepted. – dmikam Sep 09 '20 at 06:40
  • 1
    @dmikam i think there is no other way round i could find out,the tip is to make custom directives and use them in you html, then if you want to perform any css or behavioural changes then you have one place to change it, by the way thanks for encouraging me. – Saddam Sep 09 '20 at 08:21
  • @dmikam did your problem get solved?bounty is expiring? – Saddam Sep 13 '20 at 14:59
  • 1
    In the end, I am accepting your answer wind the bounty because, even if it's not exactly answers my question, but suggests using Directives (also suggested by other people) with good example and explanation which may be useful for thous who find this answer in the future. Though it looks like *there is no vue.js way* of doing what I was asking about without refactoring all existent code, but adding small directive to each element looks the least intrusive way to achieve the same result. Thanks. – dmikam Sep 13 '20 at 20:25
1

You cannot do what you are describing with VueJS. There's a fundamental difference between jQuery and VueJS.

  1. jQuery manipulates the dom
  2. VueJS uses a virtual dom

A virtual dom has the same properties as real dom, but it's must faster since its not drawing anything on the screen. It then gets compared to the real dom and updated according to what changed.

When you're constantly manipulating the dom and redrawing the site becomes slower and keeping the state of the website gets tricky. That's why VueJS uses components with props and events.

So, Vue makes the dom react to data changes rather than editing the html element itself.

Vue does have some limited ability to manipulate the dom directly through ref attribute, but it's reccomended to avoid it when possible.

The 'vue way' is to use single file components to create your site. See: https://v2.vuejs.org/v2/guide/single-file-components.html along with mixins and directives.

That means one component for each 'thing' on your website, for example <MyButton> or <NavigationBar>, etc.

If everything is set up that way then you can simply change the template or the style in that single file and every <MyButton> on your website would change. You would never change it on the fly by manipulating the dom like with jquery.

Although it's not 'clean' there is nothing stopping you to have vuejs and jquery together. So vuejs would use virtual dom, update the real dom and then jquery would manipulate it.

It will be hard to keep track of the state since there some 'magic happening' and two frameworks affecting how the website looks. e.g. some styling in vue and then jquery overriding or changing it.

tony19
  • 125,647
  • 18
  • 229
  • 307
Bergur
  • 3,962
  • 12
  • 20
  • 1
    Well I understand the difference between both and their different approaches, but I was hoping there was some "native" vue way escaping my attention of doing something similar. Looks like the closest are custom Vue.js Directives suggested in other answers. And of course you always may use both but that's something I'd like to avoid when possible. – dmikam Sep 10 '20 at 07:26
1

I think what you want will be possible in Vue 3 thanks to it's new modular architecture by overriding default renderer. You can find some examples in this great article

I don't think it's a good idea but I can imagine one should be able to create jQuery-like experience in Vue this way....

EDIT

I see your edit (added examples) and I need to add something. When I was talking about "jQuery-like experience" it was really about modifying DOM at runtime by changing how the virtual DOM is used to patch real DOM.

But all your examples are using some Vue features which are totally separate (way higher in the stack). For example adding directive or changing the prop - this has to be done on the template itself (and templates are compiled into render functions at build-time) or by replacing default createElement (usually called h) function, which is imported globally in Vue 3 so I'm not sure if its possible to replace it...

This is something really different and way way more difficult...

tony19
  • 125,647
  • 18
  • 229
  • 307
Michal Levý
  • 33,064
  • 4
  • 68
  • 86
  • Thank you for pointing this option, it looks like redefining default renderer it may be achieved but it looks kind of too much overkill for the purpose and does not even close as easy and elegant as it is in jQuery. In any case - good point! – dmikam Sep 11 '20 at 12:22
0

You can just use modern CSS: https://css-tricks.com/css-content/#example-trick-css3-tooltips

If that doesn't meet your needs, the next most simple thing to do would be to make a Component that has the visuals you want and then add it where necessary.

Vue is controlling the rendering of the DOM, so it's not going to work like jQuery which parses the DOM and then injects its own elements. If you really wanted to go crazy with it, you should be able to create a global mixin in Vue with a render function and a custom merge strategy that injects the mixin tooltips based on the inspection of the rendered component. I'm not sure I would call that "the Vue way" though.

paul
  • 81
  • 2
  • Would be nice to see some example of this kind of global mixins, as I don't really see how this could be done this way... And about the CSS way - well as I told a couple of times, the thing is not about title itself, but the concept of tweaking DOM globally as we do it with jQuery all the time. Title looked as a good and simple example of this tweaking, but only this - an example. – dmikam Sep 09 '20 at 06:49
  • Vue is primarily doing a lot of work to efficiently manage and reactively re-render portions of the DOM. If you want to add things to the DOM, the typical way of doing this is to write a component. If you want a more aspect oriented approach, you can use mixins or the new composition API. – paul Sep 09 '20 at 17:58
  • You can alter DOM in a render function (or possibly with the vm.$el inside of lifecycle hook) in any given component. Once you get that working in a single component, you can include that as part of a mixin globally. Here's an example: https://medium.com/nsoft/wrapping-vue-components-by-using-render-function-983b396a3bfa – paul Sep 09 '20 at 18:18
0

Been building apps and sites with Vue since 2015, before that I was a heavy jQuery user for years. After reviewing other answers and your responses I don't believe the answer you seek is actually related to Vue.

My first thought was "directives" like others had mentioned.

It appears you want the sugary-syntax you once had with jQuery for selecting DOM elements and applying changes in a chain-like fashion; this can be done with vanilla JavaScript. The challenge you face is working within Vue and waiting for components to finish rendering before attempting to select all possible DOM nodes. There are tricks for this, like using $nextTick() to wait for child components to finish rendering before attempting to select all the possible DOM nodes.

I can say I no longer need or want to use jQuery, and have yet to run into a use-case where I needed anything like $('a[title]').each(function(){ ... add styling, new nodes, etc... }).

If you could share more information about what it is you are trying to achieve people could likely give a more distinct answer. I do believe that there is a simple solution here, but without knowing more about your challenge it's hard to answer.

rebz
  • 2,053
  • 3
  • 16
  • 16
  • Well I am still on my way into switching my mind from jQuery way of thinking to Vue.js way and I see no way to do some things I used to do with jQuery... As an abstract example... imagine I wish to add https://github.com/Akryum/v-tooltip directive, but don't wish to refactor all my code and include v-tooltip attribute to all my html (call it lazyness). So I'd like to do something like `$('a[title]').each(function(){ $(this).attr('v-title', $(this).attr('title')); });` to automatically apply v-title directive to all my links with title attribute.... – dmikam Sep 11 '20 at 09:20
  • ..continue .... I understand I may do this BEFORE initializing Vue.js using jQuery or vanilla js but that's kind of silly. Another example: imagine I use Vuetify and for debugging purpose I wish all my v-btn components put something to console.log when clicked. In jQuery it is as easy as `$('.mybutton').click(function(){ console.log($(this).data() ); });` How should I achieve this? – dmikam Sep 11 '20 at 09:24
  • And one more example with some reactiveness in it... What if I wish to show in title/tooltip current value of each input. But again - without need to *refactor all my code* because maybe I just need it as a temporary measure or A/B test of user experience and will be removed next day/week... as some *pseudocode* this would look like `$('input').prop(':title', 'The value is {{model.value}}');` – dmikam Sep 11 '20 at 09:42
0

You don't need Vue, JavaScript or jQuery!

A tooltip can be accomplished with pure HTML/CSS.

I know this doesn't solve all your other examples. However, because a lot developers reach for JavaScript when it might not alway be necessary, I think this answer could be helpful for others.

[data-title]:hover::after {
  content: attr(data-title);
}
<p data-title="title">Hover me </p>

For a more detailed explanation see:

Marc Barbeau
  • 826
  • 10
  • 21
-1

There is Vuetify, if that's helping you out. I would do this with CSS or even LESS, look at how a title is represented in the HTML generated. If it's an attribute called v-title, for example, then a CSS rule of

[v-title] {
    /*Your rules here*/
}

will apply to elements having that attribute. You could vary it, for example

h1[v-title] {
    /*Your rules here*/
}

would apply to h1 elements having that attribute. Or,

div > [v-title] {
    /*Your rules here*/
}

would apply to any elements having that attribute, as long as their parent is a div. But, if you want a third-party design, then you can find this one, for example: https://vuetifyjs.com/en/components/subheaders/

I would advise you though to implement your own design unless you are forced not to do so, because customizing third-party designs have their limitations.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • The CSS way is not the right way for my question. As I mentioned few times - fancy title is just an example - the question is about tweaking DOM without touching HTML code (or at least with minimal modifications). And yes vuetify has a lot of functionality and components, I've used it in a couple of projects, but that's not about my question at all. – dmikam Sep 09 '20 at 07:03