Vue.js select closest div with certain classname - javascript

I have the following HTML markup:
<div id="app">
<div class="image">
<div class="overlay">
<p>Some overlay text</p>
</div>
<img src="https://placeimg.com/640/480/any" class="img-fluid">
</div>
<div class="info">
<h6 class="name">Title here</h6>
<p class="meta">Meta here</p>
</div>
<div class="info-button" #mouseover="addParentClass" #mouseout="removeParentClass">
Mouse over here!
</div>
What I would like to do is whenever someone hovers over the div with class "info-button" certain classes get added to the image above and the overlay.
I have got it working with the following Vue.js markup:
let vm = new Vue({
el: "#app",
data:{
isHovering: false
},
methods: {
addParentClass (event) {
event.target.parentElement.children[0].children[1].classList.add('active')
event.target.parentElement.children[0].children[0].classList.add('overlay-active')
},
removeParentClass (event) {
event.target.parentElement.children[0].children[1].classList.remove('active')
event.target.parentElement.children[0].children[0].classList.remove('overlay-active')
},
},
})
However it seems like a lot of redundant JS. I have tried to get it working with:
event.target.parent.closest('.overlay'.).classList.add('overlay-active')
And a lot of similar parent/children/closest selectors, however I can not seem to get the result I want. How can I get the "closest" selector to work here?
Here's a codepen with a very rough working example: Link to codepen
Edit: I want to point out that i want to use this in a loop, so I will have mulitple images and I want to make the overlay only appear on the current image.

VueJS is reactive. This means the data should drive the DOM. You should not play with the DOM yourself.
Add an active property to data;
let vm = new Vue({
el: "#app",
data:{
isHovering: false,
active: false
},
methods: {
addParentClass (event) {
this.active = true;
},
removeParentClass (event) {
this.active = false; },
},
})
And make the DOM reactive by;
<div class="overlay" :class="{'overlay-active': active}" >
<p>Some overlay text</p>
</div>
Here is the updated codepen
https://codepen.io/samialtundag/pen/Jeqooq

Related

Testdome Vue question - working locally but not passing test

I am practicing for a vue TestDome test this week. One of the practice questions is as follows :
An image gallery is a set of images with corresponding remove buttons. This is the HTML code for a gallery with two images:
<div>
<div class="image">
<img src="https://www.testdome.com/files/resources/12362/aff5c408-79f8-4220-9769-8b4cde774c98.jpg">
<button class="remove">X</button>
</div>
<div class="image">
<img src="https://www.testdome.com/files/resources/12362/dbd97d8f-cc11-48bd-bf21-b8762a39a55e.jpg">
<button class="remove">X</button>
</div>
</div>
Implement the image-gallery component that accepts a links prop and renders the gallery described above so that the first item in the links prop is the src attribute of the first image in the gallery. It should also implement the following logic: When the button is clicked, the image that is in the same div as the button should be removed from the gallery.
The component should be usable with any root Vue instance.
For example, after the first image has been removed from the gallery above, it's HTML code should look like this:
<div>
<div class="image">
<img src="https://www.testdome.com/files/resources/12362/dbd97d8f-cc11-48bd-bf21-b8762a39a55e.jpg">
<button class="remove">X</button>
</div>
</div>
Here is my solution, which when run locally, renders the two images and buttons, as well as deletes them when clicked.
<!DOCTYPE html>
<html>
<head>
<title>Image Gallery</title>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<image-gallery :links="links" #remove-link="removeLink"/>
</div>
<script>
// Create image-gallery component that accepts link prop
Vue.component('image-gallery', {
props: ['links'],
template: `
<div>
<div v-for="link in links" class="image">
<img :src="link" />
<button #click="removeLink(link)" class="remove">X</button>
</div>
</div>
`,
methods: {
removeLink (link) {
this.$emit('remove-link', link)
}
}}
);
var vm = new Vue({
el: "#app",
data () {
return {
links: ['https://www.testdome.com/files/resources/12362/aff5c408-79f8-4220-9769-8b4cde774c98.jpg', 'https://www.testdome.com/files/resources/12362/dbd97d8f-cc11-48bd-bf21-b8762a39a55e.jpg']
}
},
methods: {
removeLink(link) {
this.links = this.links.filter(l => l !== link)
}
}
}
);
// Example case
let firstButton = document.querySelectorAll("div.image > button")[0];
if(firstButton) {
firstButton.click();
}
setTimeout(() => console.log(document.getElementById("app").innerHTML));
</script>
</body>
</html>
The results according to testdome:
Example case: Wrong answer
ImageGallery will render the correct list: Correct answer
Remove a single image: Wrong answer
Remove multiple images: Wrong answer
Run OK, but 3 out of 4 test cases failed.
Output:
<div><div class="image"><img src="https://www.testdome.com/files/resources/12362/dbd97d8f-cc11-48bd-bf21-b8762a39a55e.jpg"> <button class="remove">X</button></div></div>
Unless I am reading the question wrong, the result is correct. What am I missing?

Print, on paper, element in a for-loop

Here's what I'm trying to accomplish:
<div v-for="columns in pageStructure"
//Print from here
<div v-for="htmlIWantPrinted in array">
...some content...
<button #click="printElementDiv()">Print</button>
</div>
//To here
</div>
I'm trying to print the specific content created in the for-loop. Button included.
Since there are multiple columns, I can't just put an id on it and I can't use ref either for the same reason, and using the element as a parameter for the method grabs the object instead of the html.
Simply add the value you passed to v-for loop and see the magic. This might not actually be what you want, but it should give a better understanding of what you're going to do. This is just enough.
var app = new Vue({
el: '#app',
data: {
pageStructure: ['Welcome', 'to', 'vue', 'world']
},
methods: {
printElementDiv(el) {
console.log(el)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="column in pageStructure">
<h1>Div: {{column}}</h1>
<button #click="printElementDiv(column)">Print</button>
</div>
</div>
I ended up using #RohìtJíndal' answer from the comments:
<div v-for="columns in pageStructure"
//Print from here
<div v-for="htmlIWantPrinted in array" :id="htmlIWantPrinted.id">
...some content...
<button #click="printElementDiv()">Print</button>
</div>
//To here
</div>
If you don't have a variable available, you can make one and increment it as part of the loop.

Make content editable div get focus on button click

I have a content editable div that I'm using to retrieve some data.
<div contenteditable="true" ref="test">/*SomeText*/</div>
After running the focus through the ref with this.$refs.test.focus(). It didn't work and I get an error on the console (Cannot read properties of undefined reading focus)
How can I trigger a focus effect on content editable div?
The attribute for content editable should not be content-editable="true", but instead contenteditable="true".
const App = {
el: '#app',
methods: {
focusDiv() {
this.$refs.test.focus()
}
}
}
const app = new Vue(App)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div contenteditable="true" ref="test">My Div</div>
<button #click="focusDiv">Focus on div</button>
</div>

Tabbed navigation in Vue.Js

I'm trying to create a basic tabbed navigation system with Vue. I'm capturing user's navigation choice and calling a function which changes a certain data. That data is eventually going to determine which tab should be active. So far I have this:
HTML
<div id="app">
<div id="nav">
Home
Info
</div>
<div class="content">
<div class="boxes" id="home" v-bind:class="choice">
</div>
<div class="boxes" id="info" v-bind:class="choice">
</div>
</div>
</div>
Vue
new Vue({
el: '#app',
data: {
choice: 'homeActive' // Home is chosen by default
},
methods: {
makeActive: function(val) {
this.choice = val;
}
}
});
At the moment, if the user clicks on Home link, both home and info divs get homeActive class, this is because I couldn't manage to return two statements with my methods with this basic logic:
enable tab1 & disable tab2 || enable tab 2 & disable tab1
I'm trying different approaches but I can affect only a single state with called methods due to binding my content to a single class.
Any suggestions on how I can get this working with Vue?
The way I generally solve this is by adding another function isActiveTab like so...
new Vue({
el: '#app',
data: {
choice: 'homeActive' // Home is chosen by default
},
methods: {
makeActive: function(val) {
this.choice = val;
},
isActiveTab: function(val) {
return this.choice === val;
}
}
});
Then in your view...
<div id="app">
<div id="nav">
Home
Info
</div>
<div class="content">
<div class="boxes" id="home" v-show="isActiveTab('homeActive')">
</div>
<div class="boxes" id="info" v-show="isActiveTab('infoActive')">
</div>
</div>
</div>

Find sibling with Javascript and Hammer.js

I have made a simple system which detects double taps. I want to show a heart icon when someone double taps on an image, just like on Instagram.
This is what my code looks right now:
var elements = document.getElementsByClassName('snap_img');
[].slice.call(elements).forEach(function(element) {
var hammertime = new Hammer(element),
img_src = element.getAttribute('src');
hammertime.on('doubletap', function(event) {
alert(img_src); // this is to test if doubletap works
// Some javascript to show the heart icon
});
});
This is what the HTML looks like:
<div class="snap_item">
<div class="snap_item_following_info">
<img class="snap_item_following_img" src="res/stat/img/user/profile/small/1.fw.png" alt="#JohnDoe" />
<a class="snap_item_following_name" href="#">#JohnDoe</a>
<div class="snap_too">
</div>
</div>
<img class="snap_img" src="res/stat/img/user/snap/43/2.fw.png" alt="#ErolSimsir" />
<div class="like_heart"></div>
<div class="snap_info">
<div class="snap_text">
LA is the shit...
<a class="snap_text_hashtah" href="#">#LA_city_trip</a>
</div>
<div class="snap_sub_info">
<span class="snap_time">56 minutes ago</span>
<div class="like inactive_like">
<div class="like_icon"></div>
<div class="like_no_active">5477</div>
</div>
</div>
</div>
</div>
So when the element 'snap_img' is double tapped, I need to get the element 'like_heart' which is one line below the snap_img element. How do I get that sibling element and fade it in with JQuery?
Like this
[].slice.call(elements).forEach(function(element) {
var hammertime = new Hammer(element),
img_src = element.getAttribute('src');
hammertime.on('doubletap', function(event) {
alert(img_src); // this is to test if doubletap works
$(element).next().text('♥').hide().fadeIn();
});
});
P.S. I've added that heart text, since the sibling was empty.
On the event handler, i would do $(element).parent().find('.like_heart').fadeIn(); So the code is not dependant on the element ordering.
(To clarify to selector: take the parent element which is the div.snap_item and find an element with class like-heart inside it)

Categories