How can i implement fade and collapse in vuejs3? - css

i want to implement collapse and hidden in vuejs
but i think, ref it does not work in vue3
i am getting this error Header.vue?76f0:68 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'myText')
this is my code
<button class="border-0 navbar" #click="toggle()">
<Links
class="color-dark"
ref="myText"
:style="[isActive ? { height: computedHeight } : {}]"
/>
function toggle() {
this.isActive = !this.isActive
}
function initHeight() {
this.$refs.myText.style.height = 'auto'
this.$refs.myText.style.position = 'absolute'
this.$refs.myText.style.visibility = 'hidden'
this.$refs.myText.style.display = 'block'
const height = getComputedStyle(this.$refs['myText']).height
this.computedHeight = height
this.$refs.myText.style.position = null
this.$refs.myText.style.visibility = null
this.$refs.myText.style.display = null
this.$refs.myText.style.height = 0
}
watchEffect(async () => {
initHeight()
})
i was copying this code to vuejs3 (this worked but i need to vuejs3)
https://jsfiddle.net/rezaxdi/tgfabw65/9/

Looks like there's something more to it than what's in the code. A simple vue2=>vue3 conversion from example works just fine
example:
Vue.createApp({
data() {
return {
isActive: false,
computedHeight: 0
}
},
methods: {
toggle: function() {
this.isActive = !this.isActive;
},
initHeight: function() {
this.$refs['myText'].style.height = 'auto';
this.$refs['myText'].style.position = 'absolute';
this.$refs['myText'].style.visibility = 'hidden';
this.$refs['myText'].style.display = 'block';
const height = getComputedStyle(this.$refs['myText']).height;
this.computedHeight = height;
this.$refs['myText'].style.position = null;
this.$refs['myText'].style.visibility = null;
this.$refs['myText'].style.display = null;
this.$refs['myText'].style.height = 0;
}
},
mounted: function() {
this.initHeight()
}
}).mount("#app");
p {
height: 0;
overflow: hidden;
transition: 1s;
}
<script src="https://unpkg.com/vue#3.2.31/dist/vue.global.prod.js"></script>
<div id="app">
<p ref="myText" :style="[isActive ? { height : computedHeight } : {}]">
Now it's smooth - getting closer to BS4 collapse, but now I need to know the exact height of each section at a particular screen size to set the max-height. Over-compensating max-width work ok, but is still a bit 'jerky'. Is there a way to calculate an
elements height before starting the show/hide? If the max-height value is too small, elements overlap.
</p>
<button #click="toggle">Open / Close</button>
</div>
however, I see you are using a watchEffect, so I surmise that you might be using (some🤷‍♂️) composition API functionality. In this case, the watch will execute before mount, so it will run initHeight which will fail.
If you are using composition api, there's more things there that might cause it to not work, so you may need to show more of the code. Alternatively, you can stick to the Class API, which works same as it did in vue2.

Related

Rendered Height and Client Height aren't the same

I have a react application which contains a react component of publication cards. They represent a publication that has a title, some authors, a summary, date, and some other fields. I want each of the elements to have the same card size and each of the elements within the card to line up so the title sections are all the same height as the tallest title, the authors are the same height as the tallest of the authors and so on.
To do this, I have have an inner div that contains the content shouldn't change it's height and and outer div that I set to the highest clientHeight of all inner div elements.
I have a resize function that works, however only for when you drag and move the browser by resizing the window. When I click the maximize, restore, or snap the browser to the screen it ignores the height of the inner div and sets it to weird values. Ie, the rendered size of the content will be 85px in the chrome debugger, however clientWidth if I trace that value before I set it shows all of them for each of the inner divs as like 28 and so the outer div is being set to that value.
render() {
return (
<PubContainer>
<TitleSection
style={{ height: this.state.pubTitleHeight }}>
<InnerDiv className="innerPubTitleDiv">
{this.props.publicationData.header}
<SubtitleSection>
{this.props.publicationData.subtitle}
</SubtitleSection>
</InnerDiv>
</TitleSection>
</PubContainer>
);
}
SubtitleSection is a div and InnerDiv is the container that should have its height set to whatever is the value within. Here the rest of the code, along with the resize function.
componentDidUpdate() {
this.updatePublicationCardSizes();
}
componentDidMount() {
this._isMounted = true;
this.updatePublicationCardSizes();
window.addEventListener('resize', this.updatePublicationCardSizes.bind(this));
}
componentWillUnmount() {
this._isMounted = false;
window.removeEventListener('resize', this.updatePublicationCardSizes.bind(this));
}
updatePublicationCardSizes() {
this.setContainerHeightByClassName('innerPubTitleDiv', 'pubTitleHeight');
}
setContainerHeightByClassName(innerDivClassName, stateName) {
var classNameList = document.getElementsByClassName(innerDivClassName);
if (classNameList != undefined) {
var maxHeight = 0;
for (var index = 0; index < classNameList.length; index++) {
var divHeight = classNameList[index].clientHeight;
if (maxHeight < divHeight) {
maxHeight = divHeight;
}
}
if (this._isMounted) {
if (maxHeight > 0) {
if (this.state[stateName] != maxHeight) {
this.setState({
[stateName]: maxHeight
});
}
} else {
if (this.state[stateName] != '100%'){
this.setState({
[stateName]: '100%'
});
}
}
}
}
}
I've verified that resize is being called on all changes to the browser (drag, snap, restore, max, min). And again, the normal resizing by dragging the browser to change width does update and resize it correctly, it's only when I click the maximize button for example do I get the weird values. I'm trying to find out why just those cases don't work and how to make them work.
EDIT
I've refactored my code to use hooks, but I'm still getting the same issue when I click maximize/restore. Apparently the inner div clientHeight is still being calculated from a pre-render size, instead of setting it after the resize event has finished and rendered. Any ideas?
function PublicationCard(props) {
const [pubTitleHeight, setTitleHeight] = useState('100%');
useLayoutEffect(() => {
updatePublicationCardSizes();
window.addEventListener("resize", updatePublicationCardSizes);
return () => window.removeEventListener("resize", updatePublicationCardSizes);
});
const updatePublicationCardSizes = () => {
setTitleHeight(getContainerMaxHeightByClassName('innerPubTitleDiv'));
};
const getContainerMaxHeightByClassName = (innerDivClassName) => {
var classNameList = document.getElementsByClassName(innerDivClassName);
if (classNameList != undefined) {
var maxHeight = 0;
for (var index = 0; index < classNameList.length; index++) {
var divHeight = classNameList[index].clientHeight;
if (maxHeight < divHeight) {
maxHeight = divHeight;
}
}
if (maxHeight > 0) {
return maxHeight;
} else {
return '100%';
}
}
};
return (
<PubContainer>
<a href={`/publications/${props.publicationData.id}`} target="blank">
<div
style={{ height: pubTitleHeight }}>
<div className="innerPubTitleDiv">
{props.publicationData.header}
<span>
{props.publicationData.subtitle}
</span>
</div>
</div>
</a>
</PubContainer>
);
}

React: How to set width and height of an element in react component programatically

I want to set a container's width and height programmatically based on some calculation.
I am trying to set style of the ref i created for the element in componentDidUpdate() but it has no effect
export default class CanvasOverlay extends React.Component {
static propTypes = {
imageURI : PropTypes.string.isRequired,
};
constructor(props) {
super(props);
this.width = 0;
this.height = 0;
this.canvasOverlay = React.createRef();
}
componentDidMount() {
let image = new Image();
let that = this;
image.onload = function() {
that._updateDimension(image.width, image.height);
};
image.src = this.props.imageURI;
}
_updateDimension(imageWidth, imageHeight) {
const canvasOverlay = this.canvasOverlay.current;
//Ideally there should be some calculations to get width and
height
canvasOverlay.style.width = 480;
canvasOverlay.style.height = 400;
}
render() {
return(
<div ref={this.canvasOverlay}>
<Canvas imageURI={this.props.imageURI}/>
</div>
);
}
}
HTML element canvasOverlay should have resized but it has not
The style.width and style.height of element doesn't accept a value with a type number so you should convert it to a string and add a 'px' unit.
element.style.width = `${width}px`;
// or
element.style.width = width + 'px';
There are two problems I see with the code.
_updateDimension is invoked from componentDidMount. ComponentDidMount gets called only once in lifecycle of component, which means it will always take initial size to compute width and height. Resize of the browser will not reflect correct width and you might see overflow.
canvasOverlay.style.width & canvasOverlay.style.height will require string instead of number. for e.g. "400px"
Here is a good explanation about the lifecycle of React Component: https://medium.com/#nancydo7/understanding-react-16-4-component-lifecycle-methods-e376710e5157
Here is a working POC with the given code:
https://stackblitz.com/edit/react-hosymf?file=CanvasOverlay.js

Drag and Drop in meteor

I am trying to use drag and drop on background image in a div but nothing is working. I did not find any drag and drop module for image in meteor. Is there any module or any default function in meteor to drag a background image. After uploading image is coming in div background now i want that user can drag that image and can set it's position. This is my code where i am showing image in background after uploading.
<div id="edit-image" class="text-center {{page}} {{isIosDevices}} {{profileHeader}}" style="{{myCoverPicture}}">
{{> uploaderbg profileHeader="profileHeader" userProfile=this.profile fromProfile=true}}
</div>
======= Interact JS ==================
'click .text-center': function (e) {
var isDraggable = interact('#test-img').draggable(); // true
}
<div id="my-image" class="text-center" style="">
<img src="{{myPicture}}" id="test-img" />
</div>
=================================================
Template.dragImgBg.onCreated(function helloOnCreated () {
const instance = this;
var ImageAxis1 = Meteor.user().profile.imageAxis;
values=ImageAxis1.split(' ');
instance.offsetx = new ReactiveVar(values[0]);
instance.offsety = new ReactiveVar(values[1]);
//console.log(ImageAxis1);
// fixed in this example
instance.bgUrl = new ReactiveVar(Meteor.user().profile.coverPicture);
})
Template.dragImgBg.helpers({
offsetx() {
return Template.instance().offsetx.get()
},
offsety() {
return Template.instance().offsety.get()
},
bgUrl() {
return Template.instance().bgUrl.get()
}
})
let active = false
Template.dragImgBg.events({
'mouseup' (/* event, templateInstance */) {
active = false
},
'mouseout .img-bg-movable' (/* event, templateInstance */) {
active = false
},
'mousedown .img-bg-movable' (/* event, templateInstance */) {
active = true
},
'mousemove'(event, templateInstance) {
if (!active) {
return
}
const movementx = event.originalEvent.movementX;
const movementy = event.originalEvent.movementY;
const oldx = templateInstance.offsetx.get();
const oldy = templateInstance.offsety.get();
let data = $('#data_img_pos')[0];
data.value = (oldx + movementx)+" "+(oldy + movementy);
templateInstance.offsetx.set(oldx + movementx);
templateInstance.offsety.set(oldy + movementy);
}
})
<template name="dragImgBg">
<div id="edit-image" class="img-bg-movable bg-img text-center {{page}} {{isIosDevices}}" style="background-position: {{offsetx}}px {{offsety}}px;background-image: url({{bgUrl}});">
{{> uploaderbg profileHeader="profileHeader" userProfile=this.profile fromProfile=true}}
</div>
</template>
After realizing, that this is not trivial in Blaze using third party libraries I tried to write some custom code.
Consider the following Template:
<template name="dragImgBg">
<div class="img-bg-movable" style="background-position: {{offsetx}}px {{offsety}}px;background-image: url({{bgUrl}});"></div>
</template>
with the following (examplatory) CSS:
.img-bg-movable {
width: 600px;
height: 250px;
overflow: hidden;
border: solid 1px #AAAAAA;
cursor: grab;
}
.img-bg-movable:active:hover {
cursor: grabbing;
}
As you can see the div is dynamically accepting styles, such as background image url (the one you get from your uploaded images) and x / y offset for the position.
The values for those styles are saved in reactive sources like a ReactiveVar and provided by simple helpers:
Template.dragImgBg.onCreated(function helloOnCreated () {
const instance = this
instance.offsetx = new ReactiveVar(0)
instance.offsety = new ReactiveVar(0)
// fixed in this example
instance.bgUrl = new ReactiveVar('https://upload.wikimedia.org/wikipedia/commons/3/3f/Caldwell_68_Corona_Australis_Dark_Molecular_Cloud.jpg')
})
Template.dragImgBg.helpers({
offsetx() {
return Template.instance().offsetx.get()
},
offsety() {
return Template.instance().offsety.get()
},
bgUrl() {
return Template.instance().bgUrl.get()
}
})
In order to change these values (and thus move the image) there needs to be some events that check, whether the element has been left-mouse-pressed and the mouse is moved.
If so, the delta values of the mouse-move are added to the reactive offset x / y sources. If the mouse is released or moved outside the image the values won't be applied.
let active = false
Template.dragImgBg.events({
'mouseup' (/* event, templateInstance */) {
active = false
},
'mouseout .img-bg-movable' (/* event, templateInstance */) {
active = false
},
'mousedown .img-bg-movable' (/* event, templateInstance */) {
active = true
},
'mousemove'(event, templateInstance) {
if (!active) {
return
}
const movementx = event.originalEvent.movementX
const movementy = event.originalEvent.movementY
const oldx = templateInstance.offsetx.get()
const oldy = templateInstance.offsety.get()
templateInstance.offsetx.set(oldx + movementx)
templateInstance.offsety.set(oldy + movementy)
}
})
The originalEevnt refers to the original event that is wrapped by the Template's jQuery event. You may customize the Template your needs.
If you know for example the dimensions of the image you could stop updating the position of offsetx or offsety reach these boundaries.
If you want to make this persistent (like for a user profile page) you can save the values of bgUrl (or the image file id of the uploaded image) and the offset x / y values in a collection and load these vlaues in onCreated 's autorun routine.

alternative for jquery css in vuejs

I'm learning vuejs and trying to do all without jquery
I need to get a value of a css style line-height.
In jquery i would do:
let x = $(this).css("line-height");
How can I get this value using vuejs 2.5?
I was exploring this.$el in this structure, but can't find solution to get this value:
data: function () {
return {
lineHeight: null
}
},
mounted(){
this.lineHeight = ?
}
tl;dr
// with jQuery: $(this).css("line-height");
// with Vue:
mounted() {
this.lineHeight = window.getComputedStyle(this.$el).getPropertyValue('line-height');
}
If the component (this.$el) may be inside an iframe or popup, or if you want to be extra careful, read on.
JSFiddle demo here.
new Vue({
el: '#app',
data: {
lineHeightTLDR: '',
lineHeightFull: '',
},
mounted(){
this.lineHeightTLDR = window.getComputedStyle(this.$el).getPropertyValue('line-height');
this.lineHeightFull = this.css('line-height');
},
methods: {
css(propertyName) {
let view = this.$el.ownerDocument.defaultView;
if ( !view || !view.opener ) {
view = window;
}
let computed = view.getComputedStyle(this.$el);
return computed.getPropertyValue(propertyName) || computed[propertyName];
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<pre>lineHeight tl;dr..: {{ lineHeightTLDR }}</pre>
<pre>lineHeight full...: {{ lineHeightFull }}</pre>
</div>
Background
Simplest way to mimic jQuery is just to take a look at its source. The returned value from .css() is, roughly:
ret = computed.getPropertyValue( name ) || computed[ name ];
Which uses CSSStyleDeclaration.getPropertyValue on computed. And computed is:
return function( elem ) {
var view = elem.ownerDocument.defaultView;
if ( !view || !view.opener ) {
view = window;
}
return view.getComputedStyle( elem );
}
Which uses Window.getComputedStyle() As you can see, the returned value is something around:
ret = view.getComputedStyle(elem).getPropertyValue( name ) || view.getComputedStyle(elem)[name];
Where view is most probably window but could be something else (elem.ownerDocument.defaultView).
In the end of the day, if you want to be extra certain and do very close to jQuery.css(), use:
// with jQuery: $(this).css("line-height");
// with Vue:
mounted(){
this.lineHeight = this.css('line-height');
},
methods: {
css(propertyName) {
let view = elem.ownerDocument.defaultView;
if ( !view || !view.opener ) {
view = window;
}
let computed = view.getComputedStyle(this.$el);
ret = computed.getPropertyValue(propertyName) || computed[propertyName];
}
}
But if you know your usage does not rely on iframes or popups (as it is very unusual for a Vue instance JavaScript code to run at a window and have the $el it is attached to on another), go with the tl;dr version.

how to concisely write this javascript to show/hide a list of elements?

How to write this type of code in loop? Actually I don't want to write the same same line again and again, Is their any way to compress this code? can we write this code in loop?
function showCandidates()
{document.getElementById("cand9").style.display="block";
document.getElementById("cand10").style.display="block";
document.getElementById("cand11").style.display="block";
document.getElementById("cand12").style.display="block";
document.getElementById("cand13").style.display="block";
document.getElementById("cand14").style.display="block";
document.getElementById("cand15").style.display="block";
document.getElementById("hide_cand").style.display="block";
document.getElementById("view_cand").style.display="none";
}
function hideCandidates()
{document.getElementById("cand9").style.display="none";
document.getElementById("cand10").style.display="none";
document.getElementById("cand11").style.display="none";
document.getElementById("cand12").style.display="none";
document.getElementById("cand13").style.display="none";
document.getElementById("cand14").style.display="none";
document.getElementById("cand15").style.display="none";
document.getElementById("hide_cand").style.display="none";
document.getElementById("view_cand").style.display="block";
}
I suggest this way:
var show_ids = ["cand9", "cand10", "cand11"] // ... and so on
funciton showCandidates() {
for (var index in show_ids) {
var id = show_ids[index];
document.getElementById(id).style.display="none";
}
}
similar for hideCandidates
You should assign to your html elements a class for example
<div class="hideable" >content </div>
Then either you use JQuery or plain javascript to get all the elements that have the "hideable class attribute:
document.getElementsByClassName('hideable')
or
>$(".hideable")
Since your the two previous methods will return an array, you will have to loop through the array and apply the appropriate style attribute.
Firstly, this can be all encapsulated into one function. The function can take a parameter to assign to the display property. And obviously use some if statement in there to deal with the view_cand elements' display.
I would look into using jquery for this though, it makes selecting DOM elements (especially sets of DOM elements) a damn site easier.
I'd write the code for you here but I don't know anything about the elements you're selecting or the structure to your DOM.
Something like this?
for(i=0;i<candNumber;i++){
id= "cand" + i;
document.getElementById(id).style.display="block";
}
Try this .It'll hide/show ( the wayas you requested) by parameter given to function.
setVisibilityByClass("visible"/"invisible") - shows/hides by changing class
setVisibility("block"/"none") - shows/hides by changing styles directly
CHOOSE ONLY ONE.
css classes:
.vissible{ display: block; } .invissible{ display: none; }
Js functions:
function setVisibility(val) {
var not = new Array;
not["none"] = "block";
not["block"] = "none";
for (i = 9; i <= 15; i++){
document.getElementById("cand" + i).style.display = val;
}
document.getElementById("hide_cand").style.display = val;
document.getElementById("view_cand").style.display = not[val];
}
function setVisibilityByClass(val) {
var not = new Array;
not["invissible"] = "vissible";
not["vissible"] = "invissible";
for (i = 9; i <= 15; i++){
document.getElementById("cand" + i).setAttribute("class", val);
}
document.getElementById("hide_cand").setAttribute("class", val);
document.getElementById("view_cand").setAttribute("class", not[val]);
}
I hope this helps:
(function() {
"use strict";
var candidates = {
idx: 0,
getElement: function(id) { return document.getElementById(id); },
toggle: function(elmnts, obj) {
var idx = candidates.idx,
getElement = function(id) { return candidates.getElement(id); };
if (elmnts.length) {
while ( idx < elmnts.length ) {
getElement(elmnts[idx]).style.display = obj.display;
idx++;
}
}
}
};
var idsToHide = [
"cand9", "cand10", "cand11", "cand12",
"cand13", "cand14", "cand15", "hide_cand"
];
var idsToShow = [
"cand9", "cand10", "cand11", "cand12",
"cand13", "cand14", "cand15", "hide_cand"
];
function showCandidates() {
candidates.toggle(idsToShow, {
display: "block"
});
candidates.toggle(["view_cand"], { display: "none" });
}
function hideCandidates() {
candidates.toggle(idsToHide, {
display: "none"
});
candidates.toggle(["view_cand"], { display: "block" });
}
})();
Easy to do with jQuery:
$(document).ready(function(){
$("#candidates").toggle(function (){
$(this).text('Hide Candidates');
$.each($('.candidate'), function() {
$(this).show();
});
}, function() {
$(this).text('Show Candidates');
$.each($('.candidate'), function() {
$(this).hide();
});
});
});
HTML:
Show Candidates
<div class='candidate' id='1'>
<h1>Hello</h1>
</div>
<div class='candidate' id='2'>
<h1>Hello</h1>
</div>
<div class='candidate' id='3'>
<h1>Hello</h1>
</div>
CSS:
.candidate { display: none }
Here's a JS fiddle: http://jsfiddle.net/vbh5T/
If you don't want to use jQuery then please ignore my answer.
(1) First of all, doing these kinds of lookups is best done with jquery. Apart from being easier (see code below), it also allows you pre-calculate the set of elements to act on. This matters, because lookups by ID scan the whole document tree. Accordingly, the more elements in the page, the slower it is to recalculate the set of elements to act on.
(2) Rather than setting individual properties, it is much better to use a css class.
<style>
.invisible {display:none !important;}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8"> // <![CDATA[
$(document).ready(function(){
var hide = function(i) {i.addClass('invisible');};
var show = function(i) {i.removeClass('invisible');};
var candidates = $("#cand9, #cand10 /* etc. [...] */");
/* or, if you rejig this to set a class on all candidate elements:
var candidates = $(".candidate"); */
var hide_cand = $("#hide_cand");
var view_cand = $("#view_cand");
function showCandidates()
{
show(candidates);
show(view_cand);
hide(hide_cand);
}
});
// ]]>
</script>
I leave the corresponding hideCandidates as an exercise for the reader.

Resources