Vuejs: 100vh on mobile browsers - vue.js

I have page with min-height: 100vh
and it renders on mobile browsers with some overflow on bottom. And I use this script to fix it:
methods: {
calcVH() {
const vH = Math.max(document.documentElement.clientHeight, window.innerHeight, window.screen.height || 0)
document.getElementById('app').style.height = vH + 'px';
}
},
mounted() {
this.calcVH();
window.addEventListener('onorientationchange', this.calcVH, true);
window.addEventListener('resize', this.calcVH, true);
}
It works ok in emulator, but it doesn't work on chrome/safari mobile.
Did anyone have same problem?

Yes, I had similar issues using vh. It's a known problem.
My suggestion for you is to stop using vh on mobile and tablets in order to avoid these kind of hacks around. Use classic relative % (percentage) values instead. Since I've replaced vh with % I have no such problems on mobiles but it requires a bit more implementation effort. Using % isn't straightforward in all cases, but it pays you back since you've got a solution which works pretty everywhere in the same predictable way.

This VueJS component is designed to solve it:
https://github.com/razumnyak/vue-div-100vh
<template>
<vue100vh :css="{height: '100rvh';}">
<marquee>Your stuff goes here</marquee>
</vue100vh>
</template>
<script>
import vue100vh from 'vue-100vh'
export default {
components: { vue100vh },
}
</script>
Works for smaller percentages ... where rvh = "real viewport height".
<vue100vh :style="{ minHeight: '50rvh' }">
<marquee>This is inside a div that takes at least 50% of viewport height.</marquee>
</vue100vh>

Related

Embed pdf in Electron app with no toolbar

I must be missing something very obvious, but after several hours on SO and in the electron docs trying different things, I've not found the right solution.
For an electron app, I have 4 pdfs: 3 are going to be packaged with the electron app, 1 will come from an external website. I need to be able to display each of them at full-screen, with no toolbars or other "fluff". Each is exactly 1 "screen" in size, and this is for a VERY restricted audience so I can guarantee the viewing conditions/technology used. In terms of the needed functionality, these pdfs could just be giant images. However. Due to Reasons(TM) I cannot convert them to any other (easier to use) format.
I am currently using a BrowserWindow, and it works 80%. I can see the pdf just fine, but it's zoomed and still has a toolbar. (This will be displayed in portrait orientation, hence the odd dimensions)
$('#btn_0-0').on('click', function(e) {
e.stopPropagation()
openPDF("./app/assets/docs/TreatmentLandscape.pdf")
})
function openPDF(filePath) {
let pdfWindow = new electron.remote.BrowserWindow({
width: 1080,
height: 1920,
webPreferences: {
plugins: true
}
});
pdfWindow.loadFile(filePath)
pdfWindow.setMenu(null);
pdfWindow.on('closed', function() {
pdfWindow = null
})
}
tl;dr Another person looking for how to do frameless embedded pdfs in electron. Help :(

Vue Warning on Console : You may have an infinite update loop in a component render function

I'm a new student of Computer Science and I have a school project to do on Vue.js.
It is working, but it shows the following Warning:
[Vue warn]: You may have an infinite update loop in a component render function.
English is not my first language, so I'm sorry if I write anything wrong in advance, but I'll try to explain what I have to do on the project.
I have to create a Photo Grid/Album that shows no more than 3 photos on each line. Something like this:
Example of how it has to look
The code is like this:
<template>
<v-container>
<!-- 'v-container' est´substituindo ' div class="clubes-lista" '-->
<div class="grid-photo">
<v-container class="grey lighten-3">
<v-row
v-for="index in numberLines()"
:key="index"
class="grey lighten-3"
>
<v-col v-for="n in 3" :key="n" cols="4">
<v-card v-if="numberPhotos > checkInsertedAllPhotos()" outlined>
<v-img
src="https://i.pinimg.com/736x/96/28/b7/9628b7892fd0543782f53eeec4bae502.jpg"
alt="Bird Photo"
>
</v-img>
<div class="photo-subtitle">
<v-icon size="15">mdi-heart-outline</v-icon>
<p>The Cockatiel</p>
</div>
</v-card>
<v-card v-else></v-card>
</v-col>
</v-row>
</v-container>
</div>
</v-container>
<script>
export default {
name: "PhotoGrid",
data() {
return {
listPhotosOnGrid: [],
numberPhotos: 0,
numberTotalLines: 0,
countPhotos: 0,
};
},
computed: {},
methods: {
createPhotosList() {
var listPhotos = [];
for (var i = 0; i < 10; i++) {
var aux = {
id: i + 1,
src:
"https://i.pinimg.com/736x/96/28/b7/9628b7892fd0543782f53eeec4bae502.jpg",
subtitle: "The cockatiel",
};
listPhotos.push(aux);
}
this.listPhotosOnGrid = listPhotos;
this.numberPhotos = listPhotos.length;
},
numberLines() {
this.createPhotosList();
var photosMultipleThree = this.numberPhotos;
while (photosMultipleThree % 3 != 0) {
photosMultipleThree = photosMultipleThree + 1;
}
this.numberTotalLines = photosMultipleThree / 3;
return photosMultipleThree / 3;
},
checkInsertedAllPhotos() {
var cont = this.countPhotos++;
return cont;
},
},
};
<style scoped>
.photo-subtitle {
font-size: 10px;
display: flex;
flex-direction: row;
justify-content: left;
align-items: flex-start;
padding: 0;
margin-top: 10px;
}
.photo-subtitle p {
margin-left: 5px;
}
So... Trying to figure out what is happening, I think the Warning comes from the following line:
<v-card v-if="numberPhotos > checkInsertedAllPhotos()" outlined>
The if is because it can't show more images that what is given. For example, each line shows 3 photos, so if I don't have a total of photos that is multiple os 3, I don't want to fill all columns on the last line, only the necessary to show all the photos.
But I just think, based on some tests, I'm not sure if the Warning comes from this line.
The code works, but I feel something is not right. (It works partialy actually, because I still don't know how to get the info from the array to show, so it only loops the same thing)
I want to learn how to make it right.
So does anyone can tell me what I can do to make the Warning disappear or if I have to change everything, and make make a new logic for it to work properly.
I just want to learn.
Thank you so, so much and sorry again if this is a stupid question, or if I'm making everything wrong, hehe.
And sorry if I wrote anything wrong.
:)
I've had a look to your code. First I would recommend you to stop using the var keyword but use let or const instead (you may check the question here and have a look at the scope based on those keyword, the var scope is very unusual nowadays and it will lead you to some issues).
Regarding your particular issue of infinite update loop, the problem comes from your checkInsertedAllPhotos function. I would explain it like this :
In the v-if Vue internal mechanisms checks if the element should be rendered by triggering the function checkInsertedAllPhotos
When executing the function checkInsertedAllPhotos the data countPhotos is increased (by the way it would be better to just do
this.countPhotos ++; return this.countPhotos;
Since the Data has been modified Vue is triggering a re-render of the page
Since there is a re-render Vue checks in the v-if if the element should be rendered by triggering the function checkInsertedAllPhotos.
And it could be infinite if Vue doesn't prevent it !
I would suggest to re-work a little bit your logic indeed. However a quick solution for your problem would be that when you're checking an index in your list : for example if you want to display listPhotosOnGrid[i], you check if i<numberPhotos with a v-if and in the v-else you can display something else if you want.
I also think you could have a much simpler code, by leveraging css (for example css-grid). You can have something as simple as this :
<div v-for="n in 100">
<img v-bind:src="`https://i.pinimg.com/photos/${n}.png`">
</div>
and some css to style it and only have 3 items per rows. If you want to learn about css-grid I would recommend Mozzila documentation or the following tutoriel.
Right now I have a little trouble understanding the exact question, since there are multiples topics inside to solve but I hope my explanation on the infinite loop will help you !
Have fun while learning Vue !
Thank you everyone who helped me, and you sure helped me!!!!
I'm sorry I could only anwser today.
I think I was able to do much better on the code, I changed it a lot, specially the logic on the code. And I think it can get even better, but for sure I already made a big progress, hehe.
I was a bit reluctant of posting this question because even though that as the time that I posted it I wasn't sure of what to do, I kinda of had the feeling that was something stupid (like a stuid question), and that I would only be taking the time of anyone who stopped by my question. But I only got amazing answers, with everyone being so patient, so THANK YOU SO, SO MUCH!!
(I'm also new to StackOverflow, so I hope I'm making everything right, hehe)

how to use vue-glide-js events on nuxt and load more slides on last one

i'm new on all of these so i need help. first of all how vue-glide-js events work to begin with. its documentation just listed them without examples.
And second and more important, i wanna send an axios request at the end of my carousel and load more items. i couldn't work with events so used active slide and watched it and on last slide sent and updated my data but slide still shows the previous one. what should i do?
it is the simplified version of my code
<template>
<div>
<vue-glide v-bind="carouselOptions" v-model="active">
<vue-glide-slide v-for="i in array" :key="i">
Slide {{ i }}
</vue-glide-slide>
</vue-glide>
<p>{{active}}</p>
</div>
</template>
<script>
export default {
data(){
return{
carouselOptions:{
direction: process.env.SITE_DIRECTION,
autoplay: false,
perView: this.$device.isMobileOrTablet ? 4 : 8,
peek: { before: 0, after: 50 }
},
array: [1,2,3,4,5,6,7],
active: 0
}
},
watch:{
active(){
console.log('active = ' + this.active)
if(this.active > this.array.length - 3){
this.array= [1,2,3,4,5,6,7,8,9,10,11,12,13,14]
}
}
},
mounted(){
//
}
}
</script>
as you can see i manually added more items to my array but glide still show 7 items. i know it has something to do with mounting but dont know what to do.
and one more thing. if there is any better carousel that support rtl and breakpoint (items per view on different width) i would be happy to know. tanx
To use call a method at the end of slides just using #glide:run-end:
<template>
<div>
<vue-glide v-bind="carouselOptions" v-model="active" #glide:run-end="myMethod">
<vue-glide-slide v-for="i in array" :key="i">
Slide {{ i }}
</vue-glide-slide>
</vue-glide>
</div>
</template>
to show the new slides, it's a bit tricky! as far as i know the only way is to refresh (reload) the component! so put a key on vue-glide and change it after each new added slides. the only problem is, if there is images on slides, old ones also will be load again. (it's a suggestion and didn't tried my self yet, but maybe with a caching system, it may be solved)

How to fix navbar overlap on dropdown items with a vuepress site

There's always an overlap with Navbar dropdown when more than one is clicked. It focuses and takes a few minutes to clear this becomes a problem because it causes clutter.
The configuration for this in the Vuepress docs is just to add navbar items and ariaLabel any know how I can stop this behaviour.
themeConfig: {
nav: [
{
text: 'Languages',
ariaLabel: 'Language Menu',
items: [
{ text: 'Chinese', link: '/language/chinese/' },
{ text: 'Japanese', link: '/language/japanese/' }
]
}
]
}
Here's an example
To answer your question one would need to address two distinct issues:
how do I run custom JavaSCript in VuePress?
how do I close any previously open dropdowns on click in my current VuePress theme, using JavaScript?
For the first problem there are several solutions (one of them being by using a custom component with code run in its mounted() hook, but this would require you to include that component in every page and make sure it doesn't run more than one time (since you want to bind events to elements).
I believe the cleanest way would be by adding a <script> to <head> which can be achieved by adding this to the head prop of your .vuepress/config.js export:
head: [
// ...existing stuff, if any,
['script', {}, `
(function() {
// your code here...
})();
`]
]
However, there are a few problems with the above solution. Firstly, it's going to be run as soon as it's parsed, and that's inside the <head> tag. Which means none of the contents of your page are rendered yet. And the second problem is you're in a template literal. You don't really want to be writing JavaScript code in a template literal. Ideally you should be able to put your code in a '.js' file and append it as a <script> tag.
In order to do that, you need to create a .vuepress/public/ folder, if you don't already have one. Place your .js file in there (I used test.js but feel free to name it as you like). Modify the above code to:
['script', {}, `
(function() {
var s = document.createElement('script');
s.src = './test.js';
var h = document.querySelector('head');
h.appendChild(s);
})();
`]
Change ./test.js to your file's name.
Now your file has clean JavaScript and the door is open. Your code executes in the window object context.
To answer the second part of your question, well..., it largely depends on the theme you are using. If you're using the default theme (which seems to be the case, from the SS you posted), this should work, if placed inside your .js file:
document.addEventListener('DOMContentLoaded', fixDropDowns);
function fixDropDowns() {
document.body.addEventListener('click', (ev) => {
const header = document.querySelector('header');
if (header) {
const dds = header.querySelectorAll('.dropdown-wrapper');
[...dds].forEach(el => el.classList.remove('open'));
const curr = ev.target.closest('.dropdown-wrapper');
if (curr) {
curr.classList.add('open');
}
}
})
}
But it's based on a close inspection of the generated markup.
Specifically on the fact the dropdowns have a class of .dropdown-wrapper and that they're opened by toggling class open on them. The above is just an example and will likely not work on other themes and might even stop working on the default theme in some future version.

How to run a jquery function but only when the browser window width is > 1024px?

This is a functionality I'd like to add to my responsive one-page website, but only when the browser width is greater than 1024px.
My knowledge of jQuery is minimal. This is the code I have now:
$(document).ready(function() {
var pageWidth = $(window).width();
if (pageWidth > 1024) {
$('.footer a').mouseenter(function() {
$(this).effect("bounce", { times:1, distance:4 }, 250);
});
});
});
This is the code I have in the HTML that loads this function:
<script type="text/javascript" src="js/tiny-bounce.js"></script>
Another question, when this function isn't firing—for example when a user loads the site on a device that has < 1024px width, do they still have to load the javascript file?
All you need to do this is
instead of this
if (pageWidth > 1024) {
do this
if (pageWidth < 1024) {
Yes, you still have to load the JS code when the width is less than 1024, unless you include this code in your HTML or an existing JS script.
I believe you can call $(window).width() without $(document).ready(), so that could be one way to lessen the impact of having this extra code on small screens.