I'm making a portfolio page and I want to have a picture that fades away and displays the text about the image behind it. Which I achieved hardcoding the data.
Like this.
index.html
<div class="col-sm-6">
<h4>Freelance Work</h4>
<img src="/images/andersen.png" class="portfolio_pic" id="test_pic">
<div id="test_text">
<p>Site for a structural engineer.</p>
<p><strong>Languages:</strong> JavaScript, HTML, CSS</p>
<p><strong>Other:</strong> Git, Bootstrap, GoDaddy Hosting</p>
</div>
<p>Andersen Engineering</p>
<p>GitHub Link</p>
</div>
styles.css
#test_text {
margin-top: -220px;
min-height: 210px
}
#test_pic {
max-height: 250px;
border: medium groove #660033;
}
app.js
$('.test_text').hide();
$('.test_pic').hover(function () {
$(this).stop().animate({
opacity: .1
}, 200);
$('.test_text').show();
}, function () {
$(this).stop().animate({
opacity: 1
}, 500);
$('.test_text').hide();
});
The problem is when I bring in the same info from my mongoose database, using this code
index.html
<div class="col-sm-6">
<div class="list-group" id="portfolio_items-list">
<script id="portfolio_items-template" type="text/x-handlebars-template">
{{#each portfolio_items}}
<h4>{{title}}</h4>
<img src={{image}} class="portfolio_pic" id="test_pic">
<div id="test_text">
<p>{{description}}</p>
<p><strong>Languages: </strong>{{language}}</p>
<p><strong>Frameworks: </strong>{{framework}}</p>
<p><strong>Libraries: </strong>{{library}}</p>
<p><strong>Database: </strong>{{database}}</p>
<p><strong>Other: </strong> {{other}}</p>
</div>
<p><a href={{sitelink}}>{{sitename}}</a></p>
<p><a href={{githublink}}>GitHub Link</a></p>
{{/each}}
</script>
</div>
</div>
app.js
var source = $('#portfolio_items-template').html();
var template = Handlebars.compile(source);
//get all database items
$.get(baseUrl, function (data) {
var dataHtml = template({ portfolio_items: data});
$("#portfolio_items-list").append(dataHtml);
});
then there aren't unique ID's for the test_pic and test_text id's so the javascript hide/show/opacity trick doesn't work. I was thinking if I could bring a template into the app.js and load each portfolio_items database id as a unique id for the hide/show/opacity js code, then it might work. the other alternative would be to make unique ids appear in the index/html file via the handlebars template and then copy the hide/show/opacity js code each time with that id hardcoded, but that would be very not DRY.
Any ideas?
Thanks!
The index of an {{each}} loop in handlebars is available by {{#index}} so you can do something like id="test-pic-{{#index}} to generate unique ids.
FWIW you can also accomplish the effects you are creating in just .css (see below).
.container {
width:50%;
height:250px;
overflow: hidden;
background: rgba(0, 0, 0, 0.5);
transition: all .3s ease;
}
.container:hover {
background: rgba(0, 0, 0, 0.1);
}
.text {
font-size: 2em;
opacity: 0;
transition: all .3s ease;
}
.container:hover .text {
opacity:1;
}
<div class="container">
<div class="text">
<p>hello</p>
<p>test</p>
</div>
</div>
Related
The intension is to have a two column accordion, without limiting the "expand" field to the left or right column. The catch is that there will be multiple on one page. This is already created, but only button 1 is working. With the way my JS is going, it will get very very repetitive - I am looking for assistance with re-writing the JS to be multiple click friendly. Fiddle: https://codepen.io/ttattini/pen/abLzaaY
EDIT: It would also be perfect if one dropdown would close as the next is opened
HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="row">
<div id="column">
<button id="button">I am Button #1</button>
<button id="button">I am Button #3</button>
</div>
<div id="column">
<button id="button">I am Button #2</button>
<button id="button">I am Button #4</button>
</div>
</div>
<div id="hidden">
<p id="content"> So here I am #1</p>
</div>
<div id="hidden">
<p id="content"> So here I am #2</p>
</div>
<div id="hidden">
<p id="content"> So here I am #3</p>
</div>
<div id="hidden">
<p id="content"> So here I am #4</p>
</div>
CSS
#hidden {
background: #ccc;
margin-top: 2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
#button {
padding: 10px;
margin-top: 5px;
width:50%;
margin-left: 10%;
cursor: pointer;
}
#row {
display: flex;
}
#column {
flex: 50%;
}
JS
$(function() {
var b = $("#button");
var w = $("#hidden");
var l = $("#content");
b.click(function() {
if (w.hasClass('open')) {
w.removeClass('open');
w.height(0);
} else {
w.addClass('open');
w.height(l.outerHeight(true));
}
});
});
The biggest issue is that you're using IDs when you should be using classes. IDs must be unique to each element in a page. When you repeat an ID, JS will only target the first element using that ID. That's why only the first one is working.
The second issue is that, because of the way the script is written, it will only target a single element. What you need to do is get all the elements you want to target by something like their class name and then loop through them, applying the event listener to each one and its appropriate children.
EDIT: Here is an example from some code I wrote for a page with multiple accordions a few weeks ago in vanilla JS
//Below I establish a counting variable and find all the accordions on the page
const acc = document.getElementsByClassName( 'accordion' );
let i;
//Looping through each accordion
for ( i = 1; i <= acc.length; i++ ) {
//Identify target for the event listener. In this case, a heading for each accordion, which I've numbered e.g. "title-1"
const title = 'title-' + i;
const label = document.getElementById( title );
//Identify target content, in this case a list that has a unique ID e.g. "list-1"
const listNum = 'list-' + i;
const list = document.getElementById( listNum );
//Add event listener to heading that toggles the active classes
label.addEventListener( 'click', function() {
label.classList.toggle( 'accordion--active' );
});
}
Of course, there's more than one way to skin a cat, but this is a working example.
I have tracked the clicked event of each button and showed the corresponding hidden content with the use of data- attribute.
I have used vanilla JavaScipt instead of jQuery.
const buttons = document.querySelectorAll('.button');
const hiddens = document.querySelectorAll('.hidden');
buttons.forEach((btn) => {
btn.addEventListener('click', btnClicked)
function btnClicked(e) {
hiddens.forEach((hidden) => {
if(e.target.dataset.btn == hidden.dataset.content) {
hidden.classList.toggle('height')
} else {
hidden.classList.remove('height')
}
})
}
})
.hidden {
background: #ccc;
margin-top: 2%;
padding-left:2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
.hidden.height {
height: 50px;
}
.button {
padding: 10px;
color: white;
background-color: #2da6b5;
border: none;
margin-top: 5px;
width:90%;
margin-left: 5%;
cursor: pointer;
}
.button:hover {
filter: brightness(.9);
}
#row {
display: flex;
}
.column {
flex: 50%;
}
<div id="row">
<div class="column">
<button class="button" data-btn="one">I am Button #1</button>
<button class="button" data-btn="three">I am Button #3</button>
</div>
<div class="column">
<button class="button" data-btn="two">I am Button #2</button>
<button class="button" data-btn="four">I am Button #4</button>
</div>
</div>
<div class="hidden" data-content="one">
<p class="content"> So here I am #1</p>
</div>
<div class="hidden" data-content="two">
<p class="content"> So here I am #2</p>
</div>
<div class="hidden" data-content="three">
<p class="content"> So here I am #3</p>
</div>
<div class="hidden" data-content="four">
<p class="content"> So here I am #4</p>
</div>
Also, please do not use the same ID at multiple elements.
I have a handlebars template such as this
<script type="text/html" id="some-id">
<div class="some-class-1" {{#if some_condition}}style="color:blue;"{{/if}}>
</div>
<div class="some-class-2">
<div class="some-nested-class-1">
</div>
</div>
<div class="some-class-3">
</div>
</script>
I want to modify this template for example add another div after div.some-nested-class-1
I used the following code, to parse the template to dom
jQuery(function ($) {
var t = $("#some-id");
var d = $("<div>").html(t.html());
console.log(d.html());
});
but it changes {{#if}} as follows :
<div class="some-class-1" {{#if="" some_condition}}style="color:blue;" {{="" if}}="">
</div>
<div class="some-class-2">
<div class="some-nested-class-1">
</div>
</div>
<div class="some-class-3">
</div>
Using Chrome and jsfiddle here https://jsfiddle.net/wv7hzx5a/
How can I achieve this without affecting the template?
Many thanks
PS: the template is being compiled in a separate script which I do not have access to change. I want to modify the template so that when the other script compiles it, the new elements will be included.
I don"t understand what you're trying to to and what you mean by parse the template to DOM. If you want to process the template and replace the handlebar code to have html, then use handlebar to do it and not jQuery. Only handlebar can interpret and translate the handlebar instructions to have the result. Then once that you have your result you can display it with jQuery if you want. Here is one example :
var context = {
"mydata": [
{
"character-id": "Luke1",
"firstname": "Luke",
"name": "Skywalker",
"age": "20",
"rebel": true
},
{
"character-id": "Leia1",
"firstname": "Leia",
"name": "Organa",
"age": "20",
"rebel": true
},
{
"character-id": "Vader1",
"firstname": "Darth",
"name": "Vader",
"age": "45",
"rebel": false
}
]
}
// Here you process the template and put it in the DOM
var template = $('#handlebars-demo').html();
var templateScript = Handlebars.compile(template);
var html = templateScript(context);
$("#placeholder").append(html);
// Once you have inserted your code in the DOM you can now use jQuery to modify it
// For example I put a star after Leia's section
$("#Leia1").html($("#Leia1").html()+"*")
</script>
.some-class-1 {
padding: 10;
min-width: 10px;
min-height: 20px;
border: solid blue 1px;
}
.some-class-2 {
padding: 10;
min-width: 10px;
min-height: 20px;
border: solid black 1px;
}
.some-class-3 {
padding: 10;
min-width: 10px;
min-height: 20px;
border: solid green 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script type="text/html" id="handlebars-demo">
{{#each mydata}}
<div id="{{character-id}}">
<div class="some-class-1" {{#if rebel}}style="color:blue;"{{/if}}>
{{firstname}}
</div>
<div class="some-class-2" {{#if rebel}}style="color:blue;"{{/if}}>
{{name}}
</div>
<div class="some-class-3">
<div class="some-nested-class-1">
{{age}}
</div>
</div>
</div>
<br/>
{{/each}}
</script>
<div id="placeholder"></div>
I'm very new to Vue and my web dev skills are very limited, so sorry if this is a basic questions.
I just wanted to explore how I could create a draggable grid interface in the browser and found the Muuri package.
Now just following the example code in plain JavaScript/HTML the demo works as expected.
Now I try it with Vue, I get an error saying -
"TypeError: Cannot read property 'getRootNode' of null"
Here is my Vue component that should use Muuri.
<template>
<div class="grid">
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
This can be anything.
<!-- Safe zone ends -->
</div>
</div>
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
<div class="my-custom-content">
Yippee!
</div>
<!-- Safe zone ends -->
</div>
</div>
</div>
</template>
<script>
import 'web-animations-js';
import Muuri from 'muuri';
export default {
name: 'Grid',
created() {
var grid = new Muuri('.grid', {dragEnabled:true});
console.log(grid.toString());
}
}
</script>
<style scoped>
.grid {
position: relative;
}
.item {
display: block;
position: absolute;
width: 100px;
height: 100px;
margin: 5px;
z-index: 1;
background: #000;
color: #fff;
}
.item.muuri-item-dragging {
z-index: 3;
}
.item.muuri-item-releasing {
z-index: 2;
}
.item.muuri-item-hidden {
z-index: 0;
}
.item-content {
position: relative;
width: 100%;
height: 100%;
}
</style>
Any help or advice much appreciated!
Your problem is likely that the DOM hasn't loaded when the created event hook gets executed. You could try using mounted instead. I've included a snippet.
new Vue({
el: "#app",
mounted() {
var grid = new Muuri('.grid', {dragEnabled:true});
alert(grid.toString());
}
});
<script src="https://unpkg.com/web-animations-js#2.3.2/web-animations.min.js"></script>
<script src="https://unpkg.com/muuri#0.8.0/dist/muuri.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="grid">
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
This can be anything.
<!-- Safe zone ends -->
</div>
</div>
<div class="item">
<div class="item-content">
<!-- Safe zone, enter your custom markup -->
<div class="my-custom-content">
Yippee!
</div>
<!-- Safe zone ends -->
</div>
</div>
</div>
I am trying to code a central navigation that allows for 4 tiles to appear from behind an image when the image is clicked. I am having an issue in which the tiles have a CSS styling of hover that can interrupt their toggle animation and wondered how I could temporarily disable the hover while the animation is running.
https://jsfiddle.net/Destinneh/4tgs2L5y/10/
http://sendvid.com/xodtv69a A video of what I'm trying to avoid.
$('#button').click(function(){
if($(this).attr("trigger")==="0"){
$('#navDown').animate({"top":"90%"},600);
$('#navUp').animate({"top":"10%"},600);
$('#navRight').animate({"left":"10%"},600);
$('#navLeft').animate({"left":"90%"},600);
$(this).attr("trigger","1");
}
else{
$('#navUp').animate({"top":"50%"},600);
$('#navDown').animate({"top":"50%"},600);
$('#navLeft').animate({"left":"50%"},600);
$('#navRight').animate({"left":"50%"},600);
$(this).attr("trigger","0");
}
});
You can introduce new class
Use it to play with hover
Remove class when you start animation
Add class back when animation ends
For example, change JS code to code below:
const navIds = ['#navDown', '#navUp', '#navRight', '#navLeft'];
function onAnimateStart() {
for (const id of navIds) {
$(id).removeClass('hoverOn');
}
}
function onAnimateComplete() {
for (const id of navIds) {
$(id).addClass('hoverOn');
}
}
$('#button').click(function() {
onAnimateStart();
if ($(this).attr("trigger")==="0") {
$('#navDown').animate({"top":"90%"}, 600);
$('#navUp').animate({"top":"10%"}, 600);
$('#navRight').animate({"left":"10%"}, 600);
$('#navLeft').animate({"left":"90%"}, 600, function() {
onAnimateComplete()
});
$(this).attr("trigger","1");
} else {
$('#navUp').animate({"top":"50%"}, 600);
$('#navDown').animate({"top":"50%"}, 600);
$('#navLeft').animate({"left":"50%"}, 600);
$('#navRight').animate({"left":"50%"}, 600, function() {
onAnimateComplete();
});
$(this).attr("trigger","0");
}
});
Change CSS part
.centreNav:hover{
transition: 0.25s;
width: 105px;
height: 105px;
border:2px solid white;
}
To:
.hoverOn:hover {
transition: 0.25s;
width: 105px;
height: 105px;
border: 2px solid white;
}
HTML part to (basically add hoverOn to your centreNav elements):
<div id="containerCent">
<a id="button" trigger="0" href="#"><img src="images/Logo.png" alt=" logo"></a>
<div id="spiralNav">
<a href="#">
<div id="navUp" class="centreNav hoverOn">
<h2></h2>
</div>
</a>
<a href="#">
<div id="navLeft" class="centreNav hoverOn">
<h2></h2>
</div>
</a>
<a href="#" target="_blank">
<div id="navRight" class="centreNav hoverOn">
<h2></h2>
</div>
</a>
<a href="#">
<div id="navDown" class="centreNav hoverOn">
<h2></h2>
</div>
</a>
</div>
</div>
JSFiddle with all changes from above:
https://jsfiddle.net/g3p2erfo/
I'm working on a random wiki viewer, and its been a slog, but i'm finally at the point where i think that at least the UI's functionality is done. The only problem is that after i fade some text on the "random" button, and replace it with an iframe which is then removed when the button is clicked again, the text doesn't seem to fade back in. Any ideas?
https://codepen.io/EpicTriffid/pen/WOYrzg
$(document).ready(function() {
//Random Button
var but1status = "closed"
var randFrame = ("#randframe")
$(".button1").on("click",function () {
var but = $(".button1");
var cross = $("#cross1");
but.animate({marginTop:"10%", width:"100%", height:"100vh"}, "fast");
$(".b1text").animate({opacity:0});
cross.delay(1000).fadeIn();
but1status = "open"
if (but1status == "open") {
setTimeout(randFrame,1000)
function randFrame (){
$(".button1").html("<iframe class='randframe' src='demo_iframe.htm' height='100%' width='100%' style='border:none'></iframe>");
$("#cross1").click(function() {
$('.button1').removeAttr('style');
$("#cross1").fadeOut('fast');
$('.randframe').remove();
$(".b1text").animate({opacity:"1"});
});
};
};
});
You are emptying the HTML of .button1 when you do:
$(".button1").html(....
In order to get it back, you need to add:
$(".button1").html('<div class="b1text">Random</div>');
after
$('.randframe').remove();
Your button is missing the text Random
When you call:
$(".button1").html(...
you are replacing the inside html of the object with the iframe.
When you remove .randframe you need then re-add the text for your button.
Instead of:
$('.randframe').remove()
you can call this which will accomplish both:
$('.button1').html('random');
Efficiency tip: You did a good job of saving references to your jquery variables but and cross, why not use them?
but.html(...
cross.click(function (){...
This line effectively replaces whatever you have in the button 1 div
$(".button1").html("<iframe class='randframe' src='demo_iframe.htm' height='100%' width='100%' style='border:none'></iframe>");
Your cross1.click function does not re-populate the button1 div, I would recommend
$("#cross1").click(function() {
$('.button1').removeAttr('style');
$('.button1').html('Random');
$("#cross1").fadeOut('fast');
$(".b1text").animate({opacity:"1"});
});
Here you go with the solution https://codepen.io/Shiladitya/pen/WOLNpw
$(document).ready(function() {
//Random Button
var but1status = "closed"
var randFrame = ("#randframe")
$(".button1").on("click",function () {
var but = $(".button1");
var cross = $("#cross1");
but.animate({marginTop:"10%", width:"100%", height:"100vh"}, "fast");
$(".b1text").fadeOut();
cross.delay(1000).fadeIn();
but1status = "open"
if (but1status == "open") {
setTimeout(randFrame,1000)
function randFrame (){
$(".button1").html("<iframe class='randframe' src='demo_iframe.htm' height='100%' width='100%' style='border:none'></iframe>");
$("#cross1").click(function() {
but.removeAttr('style');
cross.fadeOut('fast');
$('.randframe').remove();
but.html('<div class="b1text">Random</div>');
});
};
};
});
//Search Button
var but2 = "closed"
$(".button2").click(function () {
var but = $(".button2");
var btext = $(".b2text");
var cross = $("#cross2");
but.animate({marginTop:"10%", width:"100%", height:"100vh"}, "fast");
btext.fadeOut();
cross.delay(2000).fadeIn()
but2 = "open"
$("#cross2").click(function() {
$('.button2').removeAttr('style');
$('.b2text').fadeIn(1500);
$("#cross2").fadeOut('fast');
})
})
});
#spacer {
margin:0;
padding:0;
height:50px;
}
body {
min-height: 100%;
min-width: 1024px;
width:100%;
margin-top:4em;
padding:0;
background-color: teal;
}
h1 {
color:white;
font-family:"cabin";
text-align:center;
}
#cross1 {
position:relative;
font-size:3em;
color:white;
margin-top:6px;
float: left;
display:none;
}
#cross2 {
position:relative;
font-size:3em;
color:white;
margin-top:6px;
float: right;
display:none;
}
#randframe {
display:none;
}
.button1 {
position:absolute;
height:2.6em;
width:5em;
font-size:1.5em;
text-align:center;
color: white;
font-family:"cabin";
border:solid;
border-radius:25px;
padding:10px;
box-sizing:border-box;
transition: all 2s ease;
}
.button2 {
position:absolute;
right:0;
height:2.6em;
width:5em;
font-size:1.5em;
text-align:center;
color: white;
font-family:"cabin";
border:solid;
border-radius:25px;
padding:10px;
box-sizing:border-box;
transition: all 2s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<head>
<link href="https://fonts.googleapis.com/css?family=Josefin+Slab" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Crimson+Text" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Cabin" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>Wiki Viewer</h1>
</div>
</div>
</div>
<div id="spacer"></div>
<div class="container">
<div class="row">
<div class="col-xs-12">
<div class="button1">
<div class="b1text">Random</div>
</div>
<div class="button2">
<div class="b2text">Search</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="text-center">
<i id="cross1" class="glyphicon glyphicon-remove"></i>
</div>
</div>
<div class="col-xs-12">
<div class="text-center">
<i id="cross2" class="glyphicon glyphicon-remove"></i>
</div>
</div>
You need to keep the content inside the ".button1" to reuse after the iframe is removed.
Try using callbacks. So change your #cross1 fadout to
$("#cross1").fadeOut('fast',function(){
$('.randframe').remove();
$(".b1text").animate({opacity:"1"});
});
Also, this may not be affecting your code, but you're missing some semi colons after some variable declarations.
Not all methods have callbacks in JQuery, but when available, they are useful because basically it means that your code is not fired until the other thing is completely done. This happens a lot with fading and opacity.