I have 2 button (the unsmile and smile).
1. When i click to unsmile the img "flies" to left side.
2. When i click to smile the img is "flies" to right side.
But i need to do the following:
When i click on the img and drag it to the left, the img + description should "fly" to the left side and show a js alert - "You don't like this".
When i click on the img and drag it to right, the img + description should "fly" to the right side and show an alert - "You like this".
When i click on the img and drag it under the img, the img + description should "fly" down and show an alert - "You add this to favorite".
This should be done in javascript/html5/css.
It will be compiled using intelXDK to android platform.
HTML
<div id="food"></div>
<div id="control">
<div class="button no">
</div>
<div class="button yes">
</div>
</div>
</div>
JS Code
$('a[href*=#]').click(function(){
return false;
});
var animationEndEvent = "webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend";
var Food = {
wrap: $('#food'),
selectFood: [
{
name: 'Hamburger',
locaction: 'Poland, Warsaw, FoocCap',
img: "images/photo/1.jpg"
},
{
name: 'Chiness something',
locaction: 'Poland, Warsaw, FoocKnajp',
img: "images/photo/2.jpg"
},
{
name: 'Nuggets',
locaction: 'Japan, Tokyo, Yorazowa',
img: "images/photo/3.jpg"
},
{
name: 'Burger',
locaction: 'Japan, Nagasaki, Nogoiau',
img: "images/photo/4.jpg"
},
{
name: 'Chicken pice',
locaction: 'Russia, Moskow, Karmino',
img: "images/photo/5.jpg"
}
],
add: function(){
var random = this.selectFood[Math.floor(Math.random() * this.selectFood.length)];
this.wrap.append("<div class='foodChoose'><img alt='" + random.name + "' src='" + random.img + "' /><span><strong>" + random.name + "</strong>, " + random.locaction + "</span></div>");
}
}
var App = {
yesButton: $('.button.yes .trigger'),
noButton: $('.button.no .trigger'),
blocked: false,
like: function(liked){
var animate = liked ? 'animateYes' : 'animateNo';
var self = this;
if (!this.blocked) {
this.blocked = true;
$('.foodChoose').eq(0).addClass(animate).one(animationEndEvent, function() {
$(this).remove();
Food.add();
self.blocked = false;
});
}
}
};
App.yesButton.on('mousedown', function() {
App.like(true);
});
App.noButton.on('mousedown', function() {
App.like(false);
});
$(document).ready(function() {
Food.add();
});
CSS
#keyframes yes {
0% {
transform: scale(1) rotateZ(0deg);
left: 0;
}
30% {
transform: scale(1.05) rotateZ(0deg);
left: 0;
}
100% {
transform: rotateZ(45deg);
left: 400px;
}
}
.animateYes {
animation-fill-mode: both;
animation: yes 0.6s linear;
}
#keyframes no {
0% {
transform: rotateZ(360deg);
right: 0;
}
30% {
transform: scale(1.05) rotateZ(360deg);
right: 0;
}
100% {
transform: rotateZ(315deg);
right: 400px;
}
}
.animateNo {
animation-fill-mode: both;
animation: no 0.6s linear;
}
#control {
position: relative;
margin: 0 auto;
width: 250px;
top: -55%;
}
#control .button {
width: 65px;
height: 65px;
background: #fff;
position: absolute;
top: 5px;
border-radius: 50%;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
}
#control .button .trigger:active {
transform: translateY(-50%) scale(0.75);
transition: all .05s linear;
}
#control .button .trigger:before {
position: absolute;
top: 50%;
left: 50%;
transform: translateY(-50%) translateX(-50%);
font-family: 'FontAwesome';
}
#control .no {
left: 38px;
}
#control .no .trigger:before {
content: "\2639";
font-size: 40px;
color: #c33;
}
#control .yes {
right: 38px;
}
#control .yes .trigger:before {
content: "\263A";
font-size: 40px;
color: #3b7;
}
Current working version can be found here.
It's beed a while since I worked with jquery- let me know if it's a bad way to handle it so I can update/delete it?
You have already implemented an animationEnd handler
var App = {
yesButton: $('.button.yes .trigger'),
noButton: $('.button.no .trigger'),
blocked: false,
like: function(liked){
var animate = liked ? 'animateYes' : 'animateNo';
var self = this;
if (!this.blocked) {
this.blocked = true;
$('.foodChoose').eq(0).addClass(animate).one(animationEndEvent, function() {
$(this).remove();
Food.add();
self.blocked = false;
});
}
}
};
just pass the required arguments to your handler, e.g.
$('.foodChoose').eq(0).addClass(animate).one(animationEndEvent,
function(e) {
function(liked) {
$(this).remove();
alert(liked);
Food.add();
self.blocked = false;
}.bind(e.target, liked);
}
);
I'm passing the jquery custom event target here which should be identical to $('.foodChoose').eq(0)
^^ this means you will not need the original event and can remove the outer function like:
$('.foodChoose').eq(0).addClass(animate).one(animationEndEvent,
function(liked) {
$(this).remove();
alert(liked);
Food.add();
self.blocked = false;
}.bind($('.foodChoose').eq(0), liked);
);
added a fiddle including the code: https://jsfiddle.net/jLugv92t/1/ - slightly modified to bind to App. Thiy way we get rid of
var self = this;
Related
I'm developing a website using gatsby.js and it involves a slide-in animation as you scroll down. I wrote code that worked perfectly until I opened dev tools and tried it using the device toolbar.
here's a reproduction demo as well as a webpage to make it easier
https://getboundingclientrect-is-broken.netlify.app
<div class="0 space"></div>
<p class="1 slideFR"></p>
<div id="boy" class="2 slideFL"></div>
<p class="3 slideFR"></p>
<div class="4 slideFL"></div>
<div class="flx space"></div>
.slideFR {
width: 100px;
height: 100px;
background-color: #957b26;
position: relative;
left: 450px;
transform: translateX(1000px);
}
.slideFL {
width: 100px;
height: 100px;
background-color: #26958f;
position: relative;
left: 300px;
transform: translateX(-1000px);
}
.inSight {
transition: all 0.5s;
transform: translateX(0);
}
.space {
width: 100px;
height: 1500px;
background-color: aquamarine;
}
let elemsFL = document.getElementsByClassName("slideFL");
var leftiesLoaded = Array.from( { length: elemsFL.length }, (_, i) => false ); // creates array length of elemsFL full of <false>
let elemsFR = document.getElementsByClassName("slideFR");
var rightersLoaded = Array.from( { length: elemsFR.length }, (_, i) => false ); // creates array length of elemsFR full of <false>
document.addEventListener("scroll", function (event) {
let windowHeight = window.outerHeight;
console.log( "%c/* ----------- scroll ---------- */", "color: purple; font-weight: bold" );
checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
checkIfInSight(elemsFR, rightersLoaded, windowHeight);
});
/* -------------------------------- touchmove ------------------------------- */
document.addEventListener("touchmove", function (event) {
let windowHeight = window.outerHeight;
console.log( "%c/* ---------- touchmove --------- */", "color: red; font-weight: bold" );
checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
checkIfInSight(elemsFR, rightersLoaded, windowHeight);
});
function checkIfInSight(elemArray, boolArray, windowHeight) {
for (let counter = 0; counter < elemArray.length; counter++) {
const elem = elemArray[counter];
let elemRect = elem.getBoundingClientRect();
let elemPosTop = elemRect.top;
let elemPosBottom = elemPosTop + elem.scrollHeight;
if (elemPosTop <= windowHeight && elemPosBottom >= 0) {
if (!boolArray[counter]) {
console.log( "%c In Sight", "color: green", elem.classList[0] );
boolArray[counter] = true;
elem.classList.add("inSight");
} else {
console.log( "%c In Sight And Loaded", "color: yellow", elem.classList[0] );
}
} else {
console.log( elem.classList[0], "\tOut Of Sight", elemPosTop, "<=", windowHeight, "&&", elemPosBottom, ">=0\t\t\t", elem.offsetTop );
boolArray[counter] = false;
elem.classList.remove("inSight");
}
}
}
Edit:
As I'm troubleshooting this I replaced elem.offsetTop with window.scrollY which indeed made me realize that for some reason the it is not interpreting the scroll action as actually scrolling for quite a while. I still don't know what I'm doing wrong or what the issue is
thanks to EmielZuurbier's comment I found the solution IntersectionObserver API was the way to go. I even produced cleaner more optimized code.
HTML
<div class="0 space"></div>
<p class="1 slideFR toSlide"></p>
<div id="boy" class="2 slideFL toSlide"></div>
<p class="3 slideFR toSlide"></p>
<div class="4 slideFL toSlide"></div>
<div class=" space"></div>
JS
const slideDivs = document.querySelectorAll(".toSlide");
const options={
root: null,
rootMargin: "0px 2000px",
};
const observer= new IntersectionObserver(function(entries, observer){
entries.forEach(entry =>{
console.log(entry.target.classList[0],entry.isIntersecting, entry.intersectionRect);
if (entry.isIntersecting ){
entry.target.classList.add("inSight");
}else {
entry.target.classList.remove("inSight");
}
});
},options);
slideDivs.forEach(slideDiv => {
observer.observe(slideDiv);
});
CSS
.slideFR {
width: 100px;
height: 100px;
background-color: #957b26;
position: relative;
left: 200px;
transform: translateX(1000px);
}
.slideFL {
width: 100px;
height: 100px;
background-color: #26958f;
position: relative;
left: 150px;
/* visibility: hidden; */
transform: translateX(-1000px);
}
.inSight {
transition: all 0.5s;
transform: translateX(0);
}
.space {
width: 100px;
height: 1500px;
background-color: aquamarine;
}
I have a loader gif I'm trying to show/hide for ajax calls to indicate progress. It works for one function and not the other, oddly enough.
function showThis(id) {
document.getElementById(id).style.visibility = "visible";
}
function hideThis(id) {
document.getElementById(id).style.visibility = "hidden";
}
jQuery(function() {
jQuery("#testKey").click(function(event) {
// NEXT LINE DOESN'T WORK
showThis('loader2');
jQuery.ajax({
type: "POST",
dataType: "json",
url: "/api/?action=testKey",
data: {
exchange: jQuery('#setExchange').val(),
key: jQuery('#setKey').val(),
secret: jQuery('#setSecret').val()
},
success: function(data) {
if (!data.error) {
if (data.keyValid == true) {
alert("API Key is valid");
} else {
alert("Invalid key and/or secret.");
}
} else {
alert("Error: " + data.errorMsg);
}
}
});
hideThis('loader2');
return false;
});
});
jQuery(function() {
jQuery('#setSave').click(function(event) {
// THIS WORKS
showThis("loader2");
jQuery.post(
'/api/?action=bcSaveSettings', {
exchange: jQuery('#setExchange').val(),
key: jQuery('#setKey').val(),
secret: jQuery('#setSecret').val(),
},
function() {
var newKey = jQuery('#setKey').val();
var newSecret = jQuery('#setSecret').val();
var exchangeNum = jQuery('#setExchange').val();
var exchangeName = jQuery("#setExchange option:selected").text();
jQuery("#setExchange option:selected").attr('dat', newKey);
jQuery("#setExchange option:selected").attr('dat2', newSecret);
if (jQuery("#depExchange option[value='" + exchangeNum + "']").length > 0 && newKey.length < 1 && newSecret.length < 1) {
jQuery("#depExchange option[value='" + exchangeNum + "']").remove();
} else if (jQuery("#depExchange option[value='" + exchangeNum + "']").length < 1 && newKey.length > 0 && newSecret.length > 0) {
jQuery("#depExchange").append(jQuery('<option>', {
value: exchangeNum,
text: exchangeName
}));
}
hideThis("loader2");
alert("Settings saved");
}
);
event.preventDefault();
});
});
#loader1 {
background: #ecf0f1;
position: relative;
height: 100%;
visibility: hidden;
}
#loader2 {
background: #ecf0f1;
position: relative;
height: 100%;
visibility: hidden;
}
.dot {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
background: #2ecc71;
border-radius: 100%;
animation-duration: 1s;
animation-name: loader_dot;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#keyframes loader_dot {
0% {
width: 0px;
height: 0px;
}
to {
width: 50px;
height: 50px;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="testKey">test1</button>
<BR />
<BR />
<button id="setSave">test2</button>
<div id="loader2" style="visibility:hidden">
<div class="dot"></div>
</div>
Originally I was using jquery hide/show but got the same behavior. Note: Both ajax calls work properly but the loader only shows for the one as indicated in the JS comments.
You need call hideThis('loader2'); in ajax() callback.
Maybe success or error, even complete.
Example:
showThis()
jQuery.ajax({
// `success` or `error`
complete: function () {
// call `hide` when the `callback` called
hideThis()
}
})
Your code just call show and hide, It's doesn't wait for ajax execute.
The problem is the place where you are hiding the loader.you Need to hide the loader when the API results are emerged. Modified your code according to that.
function showThis(id) {
console.log('12' + id);
document.getElementById(id).style.visibility = "visible";
}
function hideThis(id) {
document.getElementById(id).style.visibility = "hidden";
}
jQuery(function() {
jQuery("#testKey").click(function(event) {
// NEXT LINE DOESN'T WORK
showThis('loader2');
jQuery.ajax({
type: "POST",
dataType: "json",
url: "/api/?action=testKey",
data: {
exchange: jQuery('#setExchange').val(),
key: jQuery('#setKey').val(),
secret: jQuery('#setSecret').val()
},
success: function(data) {
if (!data.error) {
hideThis("loader2");
if (data.keyValid == true) {
alert("API Key is valid");
} else {
alert("Invalid key and/or secret.");
}
} else {
alert("Error: " + data.errorMsg);
hideThis("loader2");
return false;
}
}
});
//hideThis('loader2');
//return false;
});
});
jQuery(function() {
jQuery('#setSave').click(function(event) {
// THIS WORKS
showThis("loader2");
jQuery.post(
'/api/?action=bcSaveSettings', {
exchange: jQuery('#setExchange').val(),
key: jQuery('#setKey').val(),
secret: jQuery('#setSecret').val(),
},
function() {
var newKey = jQuery('#setKey').val();
var newSecret = jQuery('#setSecret').val();
var exchangeNum = jQuery('#setExchange').val();
var exchangeName = jQuery("#setExchange option:selected").text();
jQuery("#setExchange option:selected").attr('dat', newKey);
jQuery("#setExchange option:selected").attr('dat2', newSecret);
if (jQuery("#depExchange option[value='" + exchangeNum + "']").length > 0 && newKey.length < 1 && newSecret.length < 1) {
jQuery("#depExchange option[value='" + exchangeNum + "']").remove();
} else if (jQuery("#depExchange option[value='" + exchangeNum + "']").length < 1 && newKey.length > 0 && newSecret.length > 0) {
jQuery("#depExchange").append(jQuery('<option>', {
value: exchangeNum,
text: exchangeName
}));
}
hideThis("loader2");
alert("Settings saved");
}
);
//event.preventDefault();
});
});
#loader1 {
background: #ecf0f1;
position: relative;
height: 100%;
visibility: hidden;
}
#loader2 {
background: #ecf0f1;
position: relative;
height: 100%;
visibility: hidden;
}
.dot {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
background: #2ecc71;
border-radius: 100%;
animation-duration: 1s;
animation-name: loader_dot;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#keyframes loader_dot {
0% {
width: 0px;
height: 0px;
}
to {
width: 50px;
height: 50px;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="testKey">test1</button>
<BR />
<BR />
<button id="setSave">test2</button>
<div id="loader2">
<div class="dot"></div>
</div>
The main problem with your first button code is "hideThis" function.
Show function is working fine but is instantly followed by "hide".
Move hide function in "success" code of ajax post and it should be fine.
The problem I encountered is I can't get any results from the jQuery UI Autocomplete form because of the variable scope. Let me show you.
// TAKE A CLOSE LOOK AT THIS METHOD
select: function(e, ui) {
$('#instant-search').text(ui.item.label);
$("#search").autocomplete("option", "source",
function(request, response) {
getAutocompleteResults(function(d) {
// DOESN'T WORK response(d);
});
// WORKS BUT IT SHOULD BE A DYNAMIC ARRAY FROM THE "D" OBJECT
// response(["anarchism", "anarchist black cross", "black rose (symbolism)", "communist symbolism", "political symbolism"]);
});
$("#search").autocomplete("search", ui.item.label);
In order to return results I have to use a function response([...]); outside the getAutocompleteResults(function(d) { ... }); function.
However, the source should be dynamic and not like the static array. In other words:
The function response(d); should return an object, which contains a few properties (title, value, extract). I have to access them by using response(d);, however, this function doesn't work inside getAutocompleteResults(function(d) { ... }); function. How can I achieve this?
There is a small snippet of code, however, the main problem is the select method. You can find this in the middle of the whole code block. I commented it out.
$(function() {
$("html").removeClass("no-js");
var autocompleteResults = [{
title: [],
extract: [],
pageId: []
}];
var capitalizeFirstLetter = function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
var changeText2 = function(e) {
var request = $("input").val() + String.fromCharCode(e.which);
$("#instant-search").text(request);
var getAutocompleteResults = function(callback) {
$.ajax({
url: "https://en.wikipedia.org/w/api.php?format=json&action=query&generator=search&gsrlimit=6&prop=extracts&origin=*&pilimit=max&exintro&explaintext&exsentences=1&gsrsearch=" +
$("#instant-search").text(),
beforeSend: function() {
$(".loading").show();
},
success: function(d) {
$(".loading").hide();
autocompleteResults[0].title = [];
autocompleteResults[0].extract = [];
autocompleteResults[0].pageId = [];
if (d.hasOwnProperty("query")) {
if (d.query.hasOwnProperty("pages")) {
$.each(d.query.pages, function(i) {
autocompleteResults[0].title.push(d.query.pages[i].title);
autocompleteResults[0].extract.push(d.query.pages[i].extract);
autocompleteResults[0].pageId.push(d.query.pages[i].pageid);
});
}
}
if (!autocompleteResults[0].length) {
$(".ui-autocomplete").hide();
}
autocompleteResults[0].title.sort(function(a, b) {
var nameA = a.toUpperCase();
var nameB = b.toUpperCase();
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
return 0;
});
autocompleteResults[0].title = autocompleteResults[0].title.map(
function(i) {
return i.toLowerCase();
}
);
callback(autocompleteResults[0]);
},
datatype: "json",
cache: false
});
};
$("#search").autocomplete({
source: function(request, response) {
getAutocompleteResults(function(d) {
var results = [],
filteredAutocompleteResults = [];
filteredAutocompleteResults = d.title.filter(function(i) {
return (
i !=
$("#instant-search")
.text()
.toLowerCase()
);
});
for (var i = 0; i < d.title.length; i++) {
results[i] = {
label: filteredAutocompleteResults[i],
extract: d.extract[i],
pageId: d.pageId[i]
};
}
if (results.length == 5) {
response(results);
} else {
response(results.slice(0, 5));
}
});
},
response: function() {
if ($("#instant-search").text()) {
$("table").css("display", "table");
$(".wikisearch-container").css("margin-top", 100);
}
},
close: function() {
if (!$(".ui-autocomplete").is(":visible")) {
$(".ui-autocomplete").show();
}
},
appendTo: ".input",
focus: function(e) {
e.preventDefault();
},
delay: 0,
// TAKE A CLOSE LOOK AT THIS METHOD
select: function(e, ui) {
$('#instant-search').text(ui.item.label);
$("#search").autocomplete("option", "source",
function(request, response) {
getAutocompleteResults(function(d) {
// DOESN'T WORK response(d);
});
// WORKS BUT IT SHOULD BE A DYNAMIC ARRAY FROM THE "D" OBJECT
// response(["anarchism", "anarchist black cross", "black rose (symbolism)", "communist symbolism", "political symbolism"]);
});
$("#search").autocomplete("search", ui.item.label);
// EVERYTHING SHOULD BE FINE BELOW THIS LINE
if ($(".search-results").css("opacity") != 1) {
$(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
$(".search-results p").text(ui.item.extract);
$(".search-results a").prop(
"href",
"https://en.wikipedia.org/?curid=" + ui.item.pageId
);
$(".search-results").css("opacity", 1);
} else if (
$(".search-results h4")
.text()
.toLowerCase() != ui.item.label
) {
$(".search-results").css("opacity", 0);
setTimeout(function() {
$(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
$(".search-results p").text(ui.item.extract);
$(".search-results a").prop(
"href",
"https://en.wikipedia.org/?curid=" + ui.item.pageId
);
$(".search-results").css("opacity", 1);
}, 500);
}
},
create: function() {
$(this).data("ui-autocomplete")._renderItem = function(ul, item) {
return $("<li>")
.append(
'<div class="ui-menu-item-wrapper"><div class="autocomplete-first-field"><i class="fa fa-search" aria-hidden="true"></i></div><div class="autocomplete-second-field three-dots">' +
item.label +
"</div></div>"
)
.appendTo(ul);
};
}
});
};
var changeText1 = function(e) {
if (
/[-a-z0-90áãâäàéêëèíîïìóõôöòúûüùçñ!##$%^&*()_+|~=`{}\[\]:";'<>?,.\s\/]+/gi.test(
String.fromCharCode(e.which)
)
) {
$("input").on("keypress", changeText2);
}
// DONT TOUCH THIS AREA, IT HAS NOTHING TO DO WITH THE PROBLEM
var getInputSelection = function(input) {
var start = 0,
end = 0;
input.focus();
if (
typeof input.selectionStart == "number" &&
typeof input.selectionEnd == "number"
) {
start = input.selectionStart;
end = input.selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
if (range) {
var inputRange = input.createTextRange();
var workingRange = inputRange.duplicate();
var bookmark = range.getBookmark();
inputRange.moveToBookmark(bookmark);
workingRange.setEndPoint("EndToEnd", inputRange);
end = workingRange.text.length;
workingRange.setEndPoint("EndToStart", inputRange);
start = workingRange.text.length;
}
}
return {
start: start,
end: end,
length: end - start
};
};
switch (e.key) {
case "Backspace":
case "Delete":
e = e || window.event;
var keyCode = e.keyCode;
var deleteKey = keyCode == 46;
var sel, deletedText, val;
val = this.value;
sel = getInputSelection(this);
if (sel.length) {
// 0 kai paprastai trini po viena o 1 ar daugiau kai select su pele trini
$("#instant-search").text(
val.substr(0, sel.start) + val.substr(sel.end)
);
} else {
$("#instant-search").text(
val.substr(0, deleteKey ? sel.start : sel.start - 1) +
val.substr(deleteKey ? sel.end + 1 : sel.end)
);
}
break;
case "Enter":
if ($("#instant-search").text()) {
console.log("Redirecting...");
}
break;
}
if (!$("#instant-search").text()) {
$("table, .ui-autocomplete").hide();
$(".wikisearch-container").css("margin-top", "");
}
if (
$(".ui-menu-item-wrapper").hasClass("ui-state-active") &&
(e.key == "ArrowRight" || e.key == "ArrowLeft")
) {
$(".ui-autocomplete").autocomplete(""); // Error metas console ir taip neturėtų būti bet nežinau kaip padaryti kad pasirinkus elementą su <-- ar --> nepadarytų tik vieno rezultato todėl paliekam laikinai ;)
}
};
$("input").on("keydown", changeText1);
$("input").on("input", function(e) {
$("#instant-search").text($("#search").val());
});
});
html,
body {
height: 100%;
width: 100%;
}
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-image: url("http://www.part.lt/img/96816a00ec1fb87adc4ca8a04365b2b5719.jpg");
background-size: cover;
background-position: 100%;
}
.v-container {
display: table;
height: 100%;
width: 100%;
}
.v-content {
display: table-cell;
vertical-align: middle;
}
.text-center {
text-align: center;
}
.input {
overflow: hidden;
white-space: nowrap;
}
.input input#search {
width: calc(100% - 30px);
height: 50px;
border: none;
font-size: 10pt;
float: left;
color: #4f5b66;
padding: 0 15px;
outline: none;
}
.ui-autocomplete {
list-style: none;
background-color: #fff;
-webkit-user-select: none;
user-select: none;
padding: 0;
margin: 0;
width: 100% !important;
top: auto !important;
display: table;
table-layout: fixed;
}
.ui-helper-hidden-accessible {
display: none;
}
.autocomplete-first-field {
width: 15%;
display: inline-block;
}
.autocomplete-second-field {
width: 85%;
display: inline-block;
text-align: left;
vertical-align: middle;
}
.three-dots {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
table {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
display: none;
table-layout: fixed;
}
table tr {
background-color: #fff;
-webkit-user-select: none;
user-select: none;
}
tr:first-child {
background-color: #ffc800;
color: #fff;
}
table td,
.ui-menu-item-wrapper {
padding: 10px 0;
}
td:nth-child(2) {
width: 85%;
text-align: left;
}
.ui-menu-item,
table {
cursor: pointer;
}
:focus {
outline: 0;
}
a {
text-decoration: none;
color: #fff;
position: relative;
}
a:before {
content: "";
position: absolute;
width: 100%;
height: 0.0625rem;
bottom: 0;
left: 0;
background-color: #fff;
visibility: hidden;
-webkit-transform: scaleX(0);
transform: scaleX(0);
-webkit-transition: all 0.3s ease-in-out 0s;
transition: all 0.3s ease-in-out 0s;
}
a:hover:before {
visibility: visible;
-webkit-transform: scaleX(1);
transform: scaleX(1);
}
.search-results {
background: #fff;
margin-top: 50px;
border-left: 5px solid #0ebeff;
opacity: 0;
-webkit-transition: opacity 1s;
transition: opacity 1s;
}
.search-results h4,
.search-results p {
margin: 0;
padding: 10px;
text-align: left;
}
.search-results a {
color: #0ebeff;
display: inline-block;
margin: 1rem 0;
}
.search-results a:before {
background-color: #0ebeff;
}
.wikisearch-container {
width: 65%;
margin: 0 auto;
}
/* Loading animation */
#keyframes lds-eclipse {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#-webkit-keyframes lds-eclipse {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.loading {
position: relative;
top: 9.5px;
right: 15px;
pointer-events: none;
display: none;
}
.lds-eclipse {
-webkit-animation: lds-eclipse 1s linear infinite;
animation: lds-eclipse 1s linear infinite;
width: 2rem;
height: 2rem;
border-radius: 50%;
margin-left: auto;
box-shadow: 0.08rem 0 0 #0ebeff;
}
#media (max-width: 71.875em) {
.wikisearch-container {
width: 75%;
}
}
#media (max-width: 50em) {
.wikisearch-container {
width: 85%;
}
}
#media (max-width: 17.96875em) {
.wikisearch-container {
width: 100%;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<html class="no-js">
<div class="v-container">
<div class="v-content text-center">
<div class="wikisearch-container">
<div class="input">
<input type="text" id="search" placeholder="Search...">
<div class="loading">
<div class="lds-eclipse"></div>
</div>
<button class="icon"><i class="fa fa-search"></i></button>
<table>
<tr>
<td class="fa fa-search">
<td id="instant-search" class="three-dots"></td>
</tr>
</table>
</div>
<div class="search-results">
<h4></h4>
<p></p>
<a target="_blank">Click here for more</a>
</div>
</div>
</div>
</div>
EDIT 1
After some changes, the results are shown, however, before the ajax call. How can I use response() only after the ajax was successfully completed (tried using success callback, didn't work :()?
Full project code: https://codepen.io/Kestis500/pen/zRONyw?editors=0010.
Here you can see step by step how it looks like:
How it looks like when you just reloaded the page:
Let's try entering "a":
We've got some results. Ok, let's try to click on the "anarchist symbolism" element:
Results should look like "anarchist symbolism" search. However, it returns the result of the "a" search. What if we pressed "fraktur" element?
Now it shows our previous search "anarchist symbolism" results. However, it should return elements of the "fraktur" search.
EDIT 2
I've fixed many things and removed some really non sense things from my code. However, the situation with the ajax call is still the same.
https://codepen.io/Kestis500/pen/pazppP?editors=0110
Any ideas?
EDIT 3
Fixed ajax lag (now the ajax request will be sent only after the previous ajax call).
https://codepen.io/Kestis500/pen/JpPLON?editors=0110
I had to first fix a few things in your Ajax call. We then collect the results and build an array that should be returned to response(). This will populate the AutoComplete.
First we will examine the HTML. There was some closing tags missing.
HTML
<div class="v-container">
<div class="v-content text-center">
<div class="wikisearch-container">
<div class="input ui-front">
<input type="text" id="search" placeholder="Search...">
<div class="loading">
<div class="lds-eclipse"></div>
</div>
<button class="icon">
<i class="fa fa-search"></i>
</button>
<table>
<tr>
<td class="fa fa-search"></td>
<td id="instant-search" class="three-dots"></td>
</tr>
</table>
</div>
<div class="search-results">
<h4></h4>
<p></p>
<a target="_blank">Click here for more</a>
</div>
</div>
</div>
</div>
You can see the table and it's cells all have the proper closing tags now.
I didn't make any changes to your CSS or Style.
JavaScript
$(function() {
var capitalizeFirstLetter = function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
$("#search").autocomplete({
source: function(request, response) {
var results = [];
$.ajax({
url: "https://en.wikipedia.org/w/api.php",
data: {
format: "json",
action: "query",
generator: "search",
gsrlimit: 6,
prop: "extracts|pageimages",
origin: "*",
pilimit: "max",
exintro: false,
explaintext: false,
exsentences: 1,
gsrsearch: request.term
},
beforeSend: function() {
$(".loading").show();
},
success: function(d) {
$(".loading").hide();
if (d.query.pages) {
$.each(d.query.pages, function(k, v) {
console.log(k, v.title, v.extract, v.pageid);
results.push({
label: v.title,
value: "https://en.wikipedia.org/?curid=" + v.pageid,
title: v.title,
extract: v.extract,
pageId: v.pageid
});
});
response(results);
}
},
datatype: "json",
cache: false
});
response(results);
},
close: function() {
if (!$(".ui-autocomplete").is(":visible")) {
$(".ui-autocomplete").show();
}
},
focus: function(e) {
e.preventDefault();
return false;
},
delay: 0,
select: function(e, ui) {
if ($(".search-results").css("opacity") != 1) {
$(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
$(".search-results p").text(ui.item.extract);
$(".search-results a").prop(
"href",
ui.item.value
);
$(".search-results").css("opacity", 1);
} else if (
$(".search-results h4")
.text()
.toLowerCase() != ui.item.label
) {
$(".search-results").css("opacity", 0);
setTimeout(function() {
$(".search-results h4").text(capitalizeFirstLetter(ui.item.label));
$(".search-results p").text(ui.item.extract);
$(".search-results a").prop(
"href",
ui.item.value
);
$(".search-results").css("opacity", 1);
}, 500);
}
return false;
}
}).autocomplete("instance")._renderItem = function(ul, item) {
var $item = $("<li>");
var $wrap = $("<div>").appendTo($item);
var $field1 = $("<div>", {
class: "autocomplete-first-field"
}).appendTo($wrap);
$("<i>", {
class: "fa fa-search",
"aria-hidden": true
}).appendTo($field1);
$("<div>", {
class: "autocomplete-second-field three-dots"
}).html(item.label).appendTo($wrap);
return $item.appendTo(ul);
};
});
There was a lot of things to fix and improve.
Let's start with the Ajax. You're making a call to a MediaWiki API and expecting some results. When the call would come back, it would generate warnings about pilimit. Digging into the API docs, this is a parameter specific to the pageimages properties call. To fix this, the prop value had to be extracts|pageimages. Now I get a clean set of results.
You can see I broke out the data so that I could more easily make changes and see what parameters I was sending to the API. Nothing wrong with your method, I just find this a lot easier to work with.
This is all happening inside .autocomplete() when we are populating the source. When we use function as a source, it has to follow a few guidelines:
we pass a request and response in
results must be in an array
the array can contain objects, as long as they contain at least { label, value }
our results array must be passed to response function.
A brief example:
$(selector).autocomplete({
source: function(req, resp){
var q = req.term;
// The Request is an object that contains 1 index: term
// request.term will contain the content of our search
var results = [];
// An array to store the results
$.getJSON("myapi.php", {query: q}, function(data){
$.each(data, function(key, val){
// iterate over the result data and populate our result array
results.push({
label: data.name,
value: data.url
});
resp(results);
});
});
}
});
You can sort or filter the results all you like; as long as you pass them to response in the end.
With your focus and select callbacks, you want to return false. This is discussed more here: http://jqueryui.com/autocomplete/#custom-data
We also see a good example of rendering the menu item. I switched over to making jQuery objects versus raw HTML. You do what works best for you.
Working Example: https://jsfiddle.net/Twisty/vr6gv2aw/4/
Hope this helps.
First: I'm sorry in advance for my english. This is not my first language. :)
The Situation
So, this is the deal: I'm trying to make a single button where the user can click and then it will automatically scroll down to the next DIV. Each DIV's have the class .anchor, and the one that is "selected" have another class called .anchor--selected. When you arrive at the last one, the arrow rotate to 180deg, so the user can see it will go all the way up. Yay! This part is working!
And the great part is: I don't have to give any of my div a name, since I don't know how many there will be.
But, the next part it's kind of tricky... I mean, for someone who doesn't work a lot with jQuery. (I'm learning, slowly, but I'm learning!)
The Problem
Now, when I'm in the middle of the page while scrolling and I decide instead to click, it go all the way up to the page. So, I tried a little something, and it seem to work. But when I'm in the last anchor, and I scroll too much, it giving me this error Uncaught TypeError: Cannot read property 'top' of undefined(…).
The CodePen
So here the link to the not so working anchor button with scrolling.
$(document).ready(function(){
$(".scroll-down-arrow").on("click", function(e) {
e.preventDefault();
var currentAnchor = $(".anchor--selected");;
var nextAnchor = currentAnchor.next(".anchor");
var firstAnchor = $(".anchor").first();
var lastAnchor = $(".anchor").last();
if(currentAnchor.is(lastAnchor)) {
currentAnchor.removeClass("anchor--selected");
firstAnchor.addClass("anchor--selected");
$('html, body').stop().animate({scrollTop:firstAnchor.offset().top});
$(this).removeClass("prev").addClass("next");
} else {
currentAnchor.removeClass("anchor--selected");
nextAnchor.addClass("anchor--selected");
$('html, body').stop().animate({scrollTop:nextAnchor.offset().top});
if(currentAnchor.is(lastAnchor.prev())) {
$(this).removeClass("next").addClass("prev");
}
}
});
$(window).on("scroll", function() {
var scrollPos = $(window).scrollTop();
var currentAnchor = $(".anchor--selected");;
var nextAnchor = currentAnchor.next(".anchor");
var prevAnchor = currentAnchor.prev(".anchor");
var firstAnchor = $(".anchor").first();
var lastAnchor = $(".anchor").last();
console.log("scrollPos : " + scrollPos + " currentAnchor : " + nextAnchor.offset().top);
console.log(scrollPos <= nextAnchor.offset().top);
console.log("Current anchor is last? : " + currentAnchor.is(lastAnchor));
if(scrollPos >= nextAnchor.offset().top) {
if(currentAnchor.is(lastAnchor)) {
currentAnchor.removeClass("anchor--selected");
prevAnchor.addClass("anchor--selected");
$(".scroll-down-arrow").removeClass("prev").addClass("next");
} else {
currentAnchor.removeClass("anchor--selected");
nextAnchor.addClass("anchor--selected");
if(currentAnchor.is(firstAnchor)) {
$(".scroll-down-arrow").removeClass("next").addClass("prev");
}
}
}
});
});
#one, #two, #three, #four, #five {
padding: 15px;
}
#one {
height: 500px;
background-color: #f0f8ff;
}
#two {
height: 300px;
background-color: #7fffd4;
}
#three {
height: 150px;
background-color: #deb887;
}
#four {
height: 600px;
background-color: #5f9ea0;
}
#five {
height: 1000px;
background-color: #f3b9c6;
}
.scroll-down-arrow {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #010101;
position: fixed;
bottom: 25px;
right: 25px;
cursor: pointer;
-webkit-transition: all 250ms ease-in-out;
-moz-transition: all 250ms ease-in-out;
-o-transition: all 250ms ease-in-out;
transition: all 250ms ease-in-out;
}
.scroll-down-arrow.prev {
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
-o-transform: rotate(180deg);
transform: rotate(180deg);
}
.scroll-down-arrow.next {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
.scroll-down-arrow i {
color: #f1f1f1;
font-size: 24px;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<main>
<div id="one" class="anchor anchor--selected">
This is my first div
</div>
<div id="two" class="anchor">
This is my second div
</div>
<div id="three" class="anchor">
This is my third div
</div>
<div id="four" class="anchor">
This is my fourth div
</div>
<div id="five" class="anchor">
This is my fifth div
</div>
<div class="scroll-down-arrow next"><i class="fa fa-arrow-down" aria-hidden="true"></i></div>
</main>
Conclusion
So, I've tried to reuse my code on the "scroll click" and put it on the "window scroll". But I think I'm missing something and I would like some help to get throught it.
Thank you very much and feel free to ask questions! :)
Since you aren't testing to see if there is another .anchor, jQuery is throwing an error. Just test to see if there is a next .anchor.
JQUERY
$(document).ready(function(){
$(".scroll-down-arrow").on("click", function(e) {
e.preventDefault();
var currentAnchor = $(".anchor--selected");;
var nextAnchor = currentAnchor.next(".anchor");
var firstAnchor = $(".anchor").first();
var lastAnchor = $(".anchor").last();
if(currentAnchor.is(lastAnchor)) {
currentAnchor.removeClass("anchor--selected");
firstAnchor.addClass("anchor--selected");
$('html, body').stop().animate({scrollTop:firstAnchor.offset().top});
$(this).removeClass("prev").addClass("next");
} else {
currentAnchor.removeClass("anchor--selected");
nextAnchor.addClass("anchor--selected");
$('html, body').stop().animate({scrollTop:nextAnchor.offset().top});
if(currentAnchor.is(lastAnchor.prev())) {
$(this).removeClass("next").addClass("prev");
}
}
});
$(window).on("scroll", function() {
var scrollPos = $(window).scrollTop();
var currentAnchor = $(".anchor--selected");
if(currentAnchor.next(".anchor").length){
var nextAnchor = currentAnchor.next(".anchor");
} else {
var nextAnchor = $(".anchor:first");
}
var prevAnchor = currentAnchor.prev(".anchor");
var firstAnchor = $(".anchor").first();
var lastAnchor = $(".anchor").last();
console.log("scrollPos : " + scrollPos + " currentAnchor : " + nextAnchor.offset().top);
console.log(scrollPos <= nextAnchor.offset().top);
console.log("Current anchor is last? : " + currentAnchor.is(lastAnchor));
if(scrollPos >= nextAnchor.offset().top) {
if(currentAnchor.is(lastAnchor)) {
currentAnchor.removeClass("anchor--selected");
prevAnchor.addClass("anchor--selected");
$(".scroll-down-arrow").removeClass("prev").addClass("next");
} else {
currentAnchor.removeClass("anchor--selected");
nextAnchor.addClass("anchor--selected");
if(currentAnchor.is(firstAnchor)) {
$(".scroll-down-arrow").removeClass("next").addClass("prev");
}
}
}
});
});
Fiddle: https://jsfiddle.net/yak613/up6rLqou/
Note: This doesn't work when scrolling up, I will attempt to fix that.
So, I've got this -webkit-animation rule:
#-webkit-keyframes shake {
0% {
left: 0;
}
25% {
left: 12px;
}
50% {
left: 0;
}
75% {
left: -12px;
}
100% {
left:0;
}
}
And some CSS defining some of the animation rules on my box:
#box{
-webkit-animation-duration: .02s;
-webkit-animation-iteration-count: 10;
-webkit-animation-timing-function: linear;
}
I can shake the #box like this:
document.getElementById("box").style.webkitAnimationName = "shake";
But I can't shake it again later.
This only shakes the box once:
someElem.onclick = function(){
document.getElementById("box").style.webkitAnimationName = "shake";
}
How can I re-trigger a CSS animation via JavaScript without using timeouts or multiple animations?
I found the answer based on the source code and examples at the CSS3 transition tests github page.
Basically, CSS animations have an animationEnd event that is fired when the animation completes.
For webkit browsers this event is named “webkitAnimationEnd”. So, in order to reset an animation after it has been called you need to add an event-listener to the element for the animationEnd event.
In plain vanilla javascript:
var element = document.getElementById('box');
element.addEventListener('webkitAnimationEnd', function(){
this.style.webkitAnimationName = '';
}, false);
document.getElementById('button').onclick = function(){
element.style.webkitAnimationName = 'shake';
// you'll probably want to preventDefault here.
};
and with jQuery:
var $element = $('#box').bind('webkitAnimationEnd', function(){
this.style.webkitAnimationName = '';
});
$('#button').click(function(){
$element.css('webkitAnimationName', 'shake');
// you'll probably want to preventDefault here.
});
The source code for CSS3 transition tests (mentioned above) has the following support object which may be helpful for cross-browser CSS transitions, transforms, and animations.
Here is the support code (re-formatted):
var css3AnimationSupport = (function(){
var div = document.createElement('div'),
divStyle = div.style,
// you'll probably be better off using a `switch` instead of theses ternary ops
support = {
transition:
divStyle.MozTransition === ''? {name: 'MozTransition' , end: 'transitionend'} :
// Will ms add a prefix to the transitionend event?
(divStyle.MsTransition === ''? {name: 'MsTransition' , end: 'msTransitionend'} :
(divStyle.WebkitTransition === ''? {name: 'WebkitTransition', end: 'webkitTransitionEnd'} :
(divStyle.OTransition === ''? {name: 'OTransition' , end: 'oTransitionEnd'} :
(divStyle.transition === ''? {name: 'transition' , end: 'transitionend'} :
false)))),
transform:
divStyle.MozTransform === '' ? 'MozTransform' :
(divStyle.MsTransform === '' ? 'MsTransform' :
(divStyle.WebkitTransform === '' ? 'WebkitTransform' :
(divStyle.OTransform === '' ? 'OTransform' :
(divStyle.transform === '' ? 'transform' :
false))))
//, animation: ...
};
support.transformProp = support.transform.name.replace(/([A-Z])/g, '-$1').toLowerCase();
return support;
}());
I have not added the code to detect “animation” properties for each browser. I’ve made this answer “community wiki” and leave that to you. :-)
You have to first remove the animation, then add it again. Eg:
document.getElementById("box").style.webkitAnimationName = "";
setTimeout(function ()
{
document.getElementById("box").style.webkitAnimationName = "shake";
}, 0);
To do this without setTimeout remove the animation during onmousedown, and add it during onclick:
someElem.onmousedown = function()
{
document.getElementById("box").style.webkitAnimationName = "";
}
someElem.onclick = function()
{
document.getElementById("box").style.webkitAnimationName = "shake";
}
Following the suggestion from https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Tips, remove and then add the animation class, using requestAnimationFrame to ensure that the rendering engine processes both changes. I think this is cleaner than using setTimeout, and handles replaying an animation before the previous play has completed.
$('#shake-the-box').click(function(){
$('#box').removeClass("trigger");
window.requestAnimationFrame(function(time) {
window.requestAnimationFrame(function(time) {
$('#box').addClass("trigger");
});
});
});
http://jsfiddle.net/gcmwyr14/5/
A simple but effective alternative:
HTML:
<div id="box"></div>
<button id="shake-the-box">Shake it!</button>
css:
#box{
background: blue;
margin:30px;
height:50px;
width:50px;
position:relative;
-moz-animation:shake .2s 0 linear 1;
-webkit-animation:shake .2s 0 linear 1;
}
#box.trigger{
display:table;
}
#-webkit-keyframes shake {
0% {
left: 0;
}
25% {
left: 12px;
}
50% {
left: 0;
}
75% {
left: -12px;
}
100% {
left:0;
}
}
#-moz-keyframes shake {
0% {
left: 0;
}
25% {
left: 12px;
}
50% {
left: 0;
}
75% {
left: -12px;
}
100% {
left:0;
}
}
jQuery:
$('#shake-the-box').click(function(){
$('#box').toggleClass('trigger');
});
Demo:
http://jsfiddle.net/5832R/2/
Issues:
I don't know if it works on Firefox, because the animation doesn't seem to work there...
Clone works pretty good on paused Karaoke:
On IE11 had to force a reflow (R. Krupiński's shorter version).
$('#lyrics').text("Why does it hurt when I pee?");
changeLyrics('3s');
function changeLyrics(sec) {
str = 'lyrics '+ sec + ' linear 1';
$('#lyrics').css( 'animation', str);
$('#lyrics').css( 'animation-play-state', 'running' );
$('#lyrics').replaceWith($('#lyrics').clone(true));
}
or you can use the following:
function resetAnimation(elm) {
$('#'+elm).replaceWith($('#'+elm).clone(true));
}
Reset the value first. Use reflow to apply the change without using timeout:
function shake() {
var box = document.getElementById("box");
box.style.animationName = null;
box.offsetHeight; /* trigger reflow */
box.style.animationName = "shake";
}
#keyframes shake {
0% { left: 0; }
25% { left: 12px; }
50% { left: 0; }
75% { left: -12px; }
100% { left: 0; }
}
#box {
position: absolute;
width: 75px; height: 75px;
background-color: black;
animation-duration: .02s;
animation-iteration-count: 10;
animation-timing-function: linear;
}
button {
position: absolute;
top: 100px;
}
<div id="box"></div>
<button onclick="shake()">Shake</button>
In contrast to the accepted answer that recommends animationEnd, this method resets the animation even when it's still in progress. This might be or might be not what you want.
An alternative would be to create a duplicate #keyframes animation and switch between the two:
function shake() {
var box = document.getElementById("box");
if (box.style.animationName === "shake")
box.style.animationName = "shake2";
else
box.style.animationName = "shake";
}
#keyframes shake {
0% { left: 0; }
25% { left: 12px; }
50% { left: 0; }
75% { left: -12px; }
100% { left: 0; }
}
#keyframes shake2 {
0% { left: 0; }
25% { left: 12px; }
50% { left: 0; }
75% { left: -12px; }
100% { left: 0; }
}
#box {
position: absolute;
width: 75px; height: 75px;
background-color: black;
animation-duration: .02s;
animation-iteration-count: 10;
animation-timing-function: linear;
}
button {
position: absolute;
top: 100px;
}
<div id="box"></div>
<button onclick="shake()">Shake</button>
Is there an issue with using setTimeout() to remove the class and then read it 5ms later?
svg.classList.remove('animate');
setTimeout(function() {
svg.classList.add('animate');
}, 10);
With your javascript, you could also add (and then remove) a CSS class in which the animation is declared. See what I mean ?
#cart p.anim {
animation: demo 1s 1; // Fire once the "demo" animation which last 1s
}
1) Add animation name to the #box.trigger in css
#box.trigger{
display:table;
animation:shake .2s 0 linear 1;
-moz-animation:shake .2s 0 linear 1;
-webkit-animation:shake .2s 0 linear 1;
}
2) In java-script you cannot remove the class trigger.
3) Remove the the class name by using setTimeOut method.
$(document).ready(function(){
$('#shake-the-box').click(function(){
$('#box').addClass('trigger');
setTimeout(function(){
$("#box").removeClass("trigger")},500)
});
});
4) Here is the DEMO.