Tab switching causes a little deformation during animation - slider

I made a simple vertical content slider based on tabs and found optical issue when animating . If you click on different tab, current content slides up and in the same time new one slides down. All contents have same height. The problem is that the height is changing a little bit during the animation (focus on bottom border). I don't really know how to fix it. Is there any way to prevent it?
Here is the complete code:
http://jsfiddle.net/YGY26/8/
JS:
var active = 1;
function item(id) {
if (id !== active) {
$("#description" + active).slideUp("slow");
if (active !== id) {
$("#description" + id).slideDown("slow");
active = id;
}
}
}
HTML:
<div class='slider_box' onclick='item(1);'>
<h3>Content1</h3>
<div id='description1' class='slider_content' style='display: block'>
some content to show
</div>
</div>
<div class='slider_box' onclick='item(2);'>
<h3>Content2</h3>
<div id='description2' class='slider_content'>
some content to show
</div>
</div>
<div class='slider_box' onclick='item(3);'>
<h3>Content3</h3>
<div id='description3' class='slider_content'>
some content to show
</div>
</div>
Basic CSS:
.slider_box {
position: relative;
width: 330px;
}
.slider_box h3 {
color: white;
background: black;
}
.slider_content {
height: 330px;
display: none;
}

Related

In Vue SFC link click is not triggered first time

I am using Vue 3 to show a set of links for which I am assigning event handlers dynamically(based on link id).
The issues is: The first time when any link is clicked, the corresponding event is not triggered. But subsequently clicks are perfectly working.
The updated code is below:
<script setup>
const makeSizer = ([...sizes]) => {
sizes.map((size) =>{
console.log('size-' + size);
document.getElementById('size-' + size).style.display = "";
document.getElementById('size-' + size).onclick = ((e) =>{
e.preventDefault();
document.body.style.fontSize = e.target.text + 'px';
e.target.style.display = "none";
});
});
};
function zoomIt(){
return {
zoom: makeSizer([12,14,16,18])
}
}
</script>
<template>
<div class="greeting"> {{zoom}}
<p>Some paragraph text</p>
<h1>some heading 1 text</h1>
<h2>some heading 2 text</h2>
<div class="link">
12
</div>
<div class="link">
14
</div>
<div class="link">
16
</div>
<div class="link">
18
</div>
</div>
</template>
<style>
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
.link{
padding:5px; display:inline-table;
}
.greeting {
color: red;
font-weight: bold;
}
.greeting a{
border:2px solid blue;
padding:3px;
color:white;
background-color:blue;
}
#size-12{ font-size:12px;}
#size-14{ font-size:14px;}
#size-16{ font-size:16px;}
#size-18{ font-size:18px;}
</style>
The bad news is, the way you approach it is an anti-pattern in Vue. The good news is, with some small changes you will end up with code that is much more simple to read and maintain!
You are doubling your event listeners by calling onclick() inside makeSizer() and defining click events via #click.
However, let us not just fix the bug by altering the existing code. What we want to do is to get rid of the anti-patern. So instead, we try passing the desired value of 'zoom' to the handler directly and avoid the beforementioned duplications altogether.
// Script
// We define a function that adjusts zoom value using only the value that is being passed to it as an argument
setZoom(size) {*code*}
// Template
<button #click.prevent="setZoomTo(12)">
This is a general idea. I modified your code a bit more to make it more maintainable and added comments where changes were made. I hope this helps.
Script
<script setup>
import { ref } from "vue";
const currentZoom = ref(12); // Let us set default zoom to 12
const zoomOptions = [12, 14, 16, 18]; // We define zoom options as an array to dynamically generate buttons
function setZoomTo(size) {
currentZoom.value = size; // Set current zoom value
document.body.style.fontSize = currentZoom.value + "px"; // Adjust fontSize on body
}
</script>
Template
<div class="links">
<button // We use button tag for semantic correctness
v-for="zoom in zoomOptions" // For every value in zoomOptions a button is created
:key="zoom"
:disabled="zoom === currentZoom" // If zoom value represented by the button is also currentZoom value => add disabled attribute to the button
#click.prevent="setZoomTo(zoom)" // Adjust currentZoom value according to the zoom value represented by the button
>
{{ zoom }} // Button's zoom value
</button>
</div>
Style
.links {
display: flex;
gap: 16px;
}
.links button {
border: 2px solid blue;
padding: 3px;
color: white;
background-color: blue;
cursor: pointer;
}
.links button:disabled {
opacity: 0.7; // For better UX we change button's opacity instead of hiding it
}

Dynamic Progress Bar - Vuejs

Since I am new to Vue and JS. I have some difficulties to making dynamic progress bar. This bar is kind of indication of how many quiz already take. according to he photo below.
Below is my CSS and HTML of creating bar.
.progressbar {
width: 100%;
height: 5px;
background-color: #eee;
margin: 1em auto;
transition: width 500ms;
}
HTML
<div class="progressbar">
<div class="progressbar text-center"
style="background-color: green; margin: 0; color: white;"
:style="{width: progress + '%'}">
{{ progress }}
</div>
</div>
how can I make it increase?
Place progress into the data and its working for me:
data() {
return {
progress: 70,
}
}
If you don't know how to calculate the progress just do it like so:
methods: {
updateProgres() {
this.progress=completedSteps/totalSteps*100
}
}

Run a function when clicking on a parent element but NOT child element?

I'm trying to run some code to close a modal when you click outside of the dialog box. The problem is that I have a mask that is dimming the page behind the dialog box that I am using to target the close function. It has the dialog box nested within it, so when you click on the dialog box itself, it closes it. How can I click on the dialog box normally, while still having the parent element close the modal on click?
Here's a rough idea of what I'm doing right now:
<div class="modal-background" #click="dialogOpen=false" v-if="dialogOpen">
<div class="dialog-box">
dialog content
</div>
</div>
You can determine which element is clicked with Event.target.
new Vue({
el: '#app',
data() {
return {
dialogOpen: true
}
},
methods: {
closeDialog(e) {
if (e.target.classList.contains('modal-background')) {
this.dialogOpen = false;
}
else {
// Dialog box was clicked
}
}
}
})
.modal-background {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
position: fixed;
}
.dialog-box {
width: 400px;
height: 70px;
margin: 2em auto;
background-color: lightgray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="modal-background" #click="closeDialog" v-if="dialogOpen">
<div class="dialog-box">
dialog content
</div>
</div>
</div>

jQuery animate not work in Chrome and IE

I created a div container which is relative postitioned. In this div container, there are an image which is displayed in block and is responsive image and an overlay div which is absolute positioned. Because this div container is used in responsive website scenario, it does not have a fixed dimension, instead, at any time, the dimension is determined by the dimension of the image which is changing when the size of the screen changes.
When the page is loaded, only part of the overlay is visible. When the user's mouth hovers over the overlay, the rest of the overlay needs to be displayed, and fill the entire div container. I use jQuery animate to implement the animation and it works as expected only in Firefox, and it does not work in IE, and not work in Chrome, IE and Safari.
The following is the code, and you can also find the live demo in the jsfiddle: http://jsfiddle.net/spencerfeng/K9hDn/
Here is the html:
<div id="container">
<img class="responsive-img" src="http://www.fubiz.net/wp-content/uploads/2013/07/One-Ocean-One-Breath14.jpg" alt="">
<div class="overlay">
<h1>This is title</h1>
<div class="content">There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words</div>
<a class="link" href="#">See more</a>
</div>
</div>
Here is the css:
#container {
width:400px;
margin-left:auto;
margin-right:auto;
position:relative;
}
.responsive-img {
width:100%;
height:auto;
max-width:100%;
}
.overlay {
position:absolute;
left:0;
right:0;
bottom:0px;
color:#ffffff;
text-align:center;
background: rgb(54, 25, 25);
background: rgba(54, 25, 25, .5);
padding:0 20px;
}
.content {
position:absolute;
}
a.link {
position:absolute;
bottom:10px;
right:10px;
text-indent:-9999px;
text-decoration:none;
color:#ffffff;
}
Here is the javascript:
(function($){
$('.overlay').hover(
function() {
$(this)
.animate({
top: '0'
}, 300, function() {
$(this).children('.link').css({'text-indent': '0px'});
});
}, function() {
}
);
}(jQuery));

jsplumb state machine on multiple tabs

I am designing a web page for state machine and I am using jsplumb state machine. I want to draw this state machine on multiple tabs. Tabs are created dynamically using 'New' button
<div id="tabs">
<ul>
<li>
Untitled-1
<span class="ui-icon ui-icon-close"></span>
</li>
</ul>
<div id="Untitled-1" class="content">
<div id="Untitled-1-content" class="fsm">
</div>
</div>
</div>
In the div area of class 'fsm', state machine is drawn dynamically by dragging state machine(SM) blocks and dropping it in 'fsm' div. If there are multiple tabs, after dropping SM block in fsm div, it is draggable in fsm div only on 1st tab and not in another tabs. For example, if there are 2 tabs, I can re-position SM blocks only in 1st tab, on 2nd tab when I start dragging SM block, it disappears from fsm div and its left-top coordinates become negative.
This is the code for fsm div -
$("div.fsm").droppable({
accept: ".cTemp, .rTemp",
hoverClass: "ui-state-hover",
drop: function (event, ui) {
var draggable = $(ui.draggable[0]);
if (draggable.attr("class").indexOf("rTemp") > -1) {
$("<div class='rBase' id='rect" + rectI + "'></div>").text("rect" + rectI).appendTo(this);
$("#rect" + rectI).append("<div class='ep'></div>").appendTo(this);
$("#rect" + rectI).addClass("draggable");
jsPlumb.draggable($(".rBase"),{containment: ".fsm", zIndex: 30});
} else if (draggable.attr("class").indexOf("cTemp") > -1) {
$("<div class='cBase' id='circle" + circleI + "'></div>").text("circle" + circleI).appendTo(this);
$("#circle" + circleI).append("<div class='ep'></div>").appendTo(this);
$("#circle" + circleI).addClass("draggable");
jsPlumb.draggable($(".cBase"),{containment: ".fsm", zIndex: 30});
}
jsPlumbDemo.init();
}
});
This might be because fsm div is created dynamically or there are more than 1 fsm divs. What would be the best option to handle this multitab situation ?
I tried to remove fsm div from all tabs except the active tab then it works. I can drag SM blocks from tab content even if there are more than 1 tabs since there is only 1 fsm div. But then I have to add fsm div back again to the tab when I switch to that tab.
Then which is the best way to save the tab's content before switching to any other tab and load it back when that tab is opened ?
To give my background, this is my first time to work on jquery/jsplumb so detailed explanation is very much appreciated.
Here is a couple of comments
You can use the Jquery command hasClass() instead of attr("class")indexOf
To handle tabs I guess you are using the Jquery command tabs() ? in that case you don't need to save anything.
As far as your dropping issue is concerned you should use the appendTo: property in the draggable() parameters specifying which div they are supposed to be dropped to. You can change the target tab by reacting to the jquery tab select event this way
$("#tabs").tabs({
select: function(event, ui) {
// Your code
}
});
You can play with this fiddle I made for you
http://jsfiddle.net/webaba/sy9PJ/
$(document).ready(function () {
// Calls custom select tab function, scroll down for implementation
selectTab(1);
// Block models are made draggable
function selectTab(tabIndex) {
var tabName = '#tab'+tabIndex;
$(".model").draggable({
appendTo: tabName,
helper: 'clone'
});
}
// Tabs div is formatted as tabs by jquery
$("#tabs").tabs({
select: function (event, ui) {
selectTab(ui.index + 1);
}
});
//Sets canvas as droppable
$(".tab").droppable({
accept: ".model",
drop: function (event, ui) {
var newBlock = ui.helper.clone();
$(newBlock).removeClass("model").addClass("block");
$(this).append($(newBlock));
$(newBlock).draggable({containment:$(this)});
return true;
}
});
});
and the html
<body>
<div id="container">
<div id="library">
<div class="model type1" type="1">Block1</div>
<div class="model type2" type="2">Block2</div>
</div>
<div id="tabs">
<ul>
<li>Tab1
</li>
<li>Tab2
</li>
<li>Tab3
</li>
</ul>
<div id="tab1" class="tab">Tab1</div>
<div id="tab2" class="tab">Tab2</div>
<div id="tab3" class="tab">Tab3</div>
</div>
</div>
</body>
and the CSS
#container {
width:100%;
height:300px;
}
#tabs {
height:300px;
width:70%;
float:right;
border:1px solid black;
}
.tab{
height:100%;
border:1px dotted blue;
}
#library {
border:1px solid black;
width:20%;
height:300px;
float:left;
}
.type1 {
width:50px;
height:50px;
border:1px solid green;
}
.type2 {
width:50px;
height:20px;
border:1px solid red;
}