Flickity - New dynamiclly created carousel not initialising - javascript

I want to insert new carousels onto my page dynamically, i have added a reduced test case of what im trying to do. The only real different is how the new carousel is being added, my test case it just a string which gets appended. but in my project, it will be an ajax request.
Anyway, as you can see, i am destroying all carousels on click, then once I've added the new carousel, i try and initialise the new one as well as the old one.
The problem is, the new one isn't effected. but the old one still works.
Any ideas and getting the new one to work too?
Test case: https://codepen.io/jsayner/pen/ab06bdabd3d74afc5e84cb7730d21b52
// external js: flickity.pkgd.js
var $carousel = $('.carousel').flickity();
$(document).on("click", "#button", function (e) {
$carousel.flickity('destroy');
var template = "<div class='carousel'>"
+ "<div class='carousel-cell'></div>"
+ "<div class='carousel-cell'></div>"
+ "<div class='carousel-cell'></div>"
+ "<div class='carousel-cell'></div>"
+ "<div class='carousel-cell'></div>"
+ "</div>";
$("body").append(template);
$carousel.flickity();
});
/* external css: flickity.css */
* { box-sizing: border-box; }
body { font-family: sans-serif; }
.carousel {
background: #FAFAFA;
}
.carousel-cell {
width: 66%;
height: 200px;
margin-right: 10px;
background: #8C8;
border-radius: 5px;
counter-increment: carousel-cell;
}
/* cell number */
.carousel-cell:before {
display: block;
text-align: center;
content: counter(carousel-cell);
line-height: 200px;
font-size: 80px;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/flickity#2/dist/flickity.pkgd.js"></script>
<link href="https://unpkg.com/flickity#2/dist/flickity.css" rel="stylesheet"/>
<button id="button" type="button">show carousel</button>
<div class="carousel">
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
</div>

You should change
$carousel.flickity();
to
$('.carousel').flickity();
The variable $carousel is only the instance when you fisrt init flickity, so not work for the added element.

Related

How to 'extract' css elements from a div (that is forming a grid)

I am trying to build a grid to run directly with hardware connected to a raspberry pi. On the client side, I am trying to have a button that saves the current "frame" of the grid. I'd like for this button to just gather the CSS color values (in hexadecimal form) and form an array with them, but everything I have tried hasn't worked as of yet.
I am aware of the 'style' and 'computedStyles' methods in javascript, and these may be part of the key to the question, but I'm not sure. My hunch is that the issue lies within state.
<div id="palette">
<div
class="pen"
style="background-color: #ffffff;"
onclick="setPenColor('#ffffff')"
></div>
<--- Yadayada more 'pens' -->
<div id="art">
<div class="row">
<div class="pixel" onclick="setPixelColor(this)"></div>
<div class="pixel" onclick="setPixelColor(this)"></div>
<div class="pixel" onclick="setPixelColor(this)"></div>
<div class="pixel" onclick="setPixelColor(this)"></div>
<--- Javascript -->
var penColor = 'black';
function setPenColor(pen) {
penColor = pen;
}
function setPixelColor(pixel) {
pixel.style.backgroundColor = penColor;
}
var element = document.getElementById('pixel');
var out = '';
var elementStyle = element.style;
var computedStyle = window.getComputedStyle(element, null);
for (prop in elementStyle) {
if (elementStyle.hasOwnProperty(prop)) {
out +=
' ' +
prop +
" = '" +
elementStyle[prop] +
"' > '" +
computedStyle[prop] +
"'\n";
}
}
console.log(out);
<-- CSS -->
#art {
display: table;
border-spacing: 1px;
background-color: black;
border: 5px solid black;
}
.row {
display: table-row;
}
.pixel {
display: table-cell;
background-color: white;
width: 25px;
height: 25px;
}
.pen {
display: inline-block;
width: 35px;
height: 35px;
border: 2px solid black;
border-radius: 50%;
}
My current output just gives me an error message:
Uncaught TypeError: Cannot read property 'style' of null
at grid.js:24
Like I said, I'd like to form an array of the hexadecimal values, but accessing them properly has become my first priority now.
Thanks much for any help! :)

AppendTo but each new append is unique but still uses the same jquery

I'm wondering if it's possible to on each appendTo make the new div unique but still use the same jquery.
As you can see in the mark-up below, each new div shares the same jquery so doesn't work independently.
Within my Javascript i'm selecting the ID to fire each function.
I've tried just adding + 1 etc to the end of each ID, but with that it changes the name of the ID making the new created DIV not function.
I've thought of using DataAttribues, but i'd still have the same issue having to create multiple functions all doing the same job.
Any ideas?
Thanks
$(function() {
var test = $('#p_test');
var i = $('#p_test .upl_drop').length + 1;
$('#addtest').on('click', function() {
$('<div class="file-input"><div class="input-file-container upl_drop"><label for="p_test" class="input-file-trigger">Select a file...<input type="file" id="p_test" name="p_test_' + i + '" value=""class="input-file"></label></div><span class="remtest">Remove</span><p class="file-return"></p></div>').appendTo(test);
i++;
});
$('body').on('click', '.remtest', function(e) {
if (i > 2) {
$(this).closest('.file-input').remove();
i--;
}
});
});
var input = document.getElementById( 'file-upload' );
var infoArea = document.getElementById( 'file-upload-filename' );
input.addEventListener( 'change', showFileName );
function showFileName( event ) {
// the change event gives us the input it occurred in
var input = event.srcElement;
// the input has an array of files in the `files` property, each one has a name that you can use. We're just using the name here.
var fileName = input.files[0].name;
// use fileName however fits your app best, i.e. add it into a div
textContent = 'File name: ' + fileName;
$("#input-file-trigger").text(function () {
return $(this).text().replace("Select a file...", textContent);
});
}
/*
#### Drag & Drop Box ####
*/
.p_test{
display: inline-block;
}
.upl_drop{
border: 2px dashed #000;
margin: 0px 0px 15px 0px;
}
.btn--add p{
cursor: pointer;
}
.input-file-container {
position: relative;
width: auto;
}
.input-file-trigger {
display: block;
padding: 14px 45px;
background: #ffffff;
color: #1899cd;
font-size: 1em;
cursor: pointer;
}
.input-file {
position: absolute;
top: 0; left: 0;
width: 225px;
opacity: 0;
padding: 14px 0;
cursor: pointer;
}
.input-file:hover + .input-file-trigger,
.input-file:focus + .input-file-trigger,
.input-file-trigger:hover,
.input-file-trigger:focus {
background: #1899cd;
color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
<div class="file-input">
<div class="input-file-container upl_drop">
<input class="input-file" id="file-upload" type="file">
<label tabindex="0" for="file-upload" id="input-file-trigger" class="input-file-trigger">Select a file...</label>
</div>
<div id="file-upload-filename"></div>
</div>
<button class="btn--add" id="addtest">
Add
</button>
</div>
I'd advise against using incremental id attributes. They become a pain to maintain and also make the logic much more complicated than it needs to be.
The better alternative is to use common classes along with DOM traversal to relate the elements to each other, based on the one which raised any given event.
In your case, you can use closest() to get the parent .file-input container, then find() any element within that by its class. Something like this:
$(function() {
var $test = $('#p_test');
$('#addtest').on('click', function() {
var $lastGroup = $test.find('.file-input:last');
var $clone = $lastGroup.clone();
$clone.find('.input-file-trigger').text('Select a file...');
$clone.insertAfter($lastGroup);
});
$test.on('click', '.remtest', function(e) {
if ($('.file-input').length > 1)
$(this).closest('.file-input').remove();
}).on('change', '.input-file', function(e) {
if (!this.files)
return;
var $container = $(this).closest('.file-input');
$container.find(".input-file-trigger").text('File name: ' + this.files[0].name);
});
});
.p_test {
display: inline-block;
}
.upl_drop {
border: 2px dashed #000;
margin: 0px 0px 15px 0px;
}
.btn--add p {
cursor: pointer;
}
.input-file-container {
position: relative;
width: auto;
}
.input-file-trigger {
display: block;
padding: 14px 45px;
background: #ffffff;
color: #1899cd;
font-size: 1em;
cursor: pointer;
}
.input-file {
position: absolute;
top: 0;
left: 0;
width: 225px;
opacity: 0;
padding: 14px 0;
cursor: pointer;
}
.input-file:hover+.input-file-trigger,
.input-file:focus+.input-file-trigger,
.input-file-trigger:hover,
.input-file-trigger:focus {
background: #1899cd;
color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
<div class="file-input">
<div class="input-file-container upl_drop">
<input class="input-file" type="file">
<label tabindex="0" for="file-upload" class="input-file-trigger">Select a file...</label>
</div>
<div class="file-upload-filename"></div>
</div>
<button class="btn--add" id="addtest">Add</button>
</div>
Note that I've made a couple of other optimisations to the code. Firstly it now makes a clone() of the last available .file-input container when the Add button is clicked. This is preferred over writing the HTML in the JS file as it keeps the two completely separate. For example, if you need to update the UI, you don't need to worry about updating the JS now, as long as the classes remain the same.
Also note that you were originally mixing plain JS and jQuery event handlers. It's best to use one or the other. As you've already included jQuery in the page, I used that as it makes the code easier to write and more succinct.
Finally, note that you didn't need to provide a function to text() as you're completely over-writing the existing value. Just providing the new string is fine.

Prevent auto scroll when prepend something

I have this code.
<div class="mes-all">
<div class="mes" id="1">test1</div>
</div>
<button id="add">Send</button>
And on every submit some .mes divs prepending to .mes-all div.But I want it not scroll auto,I want it maintain position same as before prepending divs above.
I have looked for solution in even this site,but couldn't find.
I can't control height() or scrollTop(), I don't know this functions very well,that's why I can't figure out the problem.
I have jsfiddle https://jsfiddle.net/83xq3b3L/
js:
var $elem = $('#list');
var height = $elem.height();
$("#add").on("click",function(){
var id=parseInt($(".mes:first").attr("id"))+1;
$(".mes-all").prepend('<div class="mes" id="'+id+'">test'+id+'</div>');
height+=30;
$(".mes-all").animate({scrollTop: height+"px"});
});
HTML:
<div id = "list" class="mes-all">
<div class="mes" id="1">test1</div>
</div>
<button id="add">Send</button>
One approach is to get the scrollHiehgt of the parent div and then scrollTop to that height.
So you code might look like this:
$("#add").on("click", function() {
var id = parseInt($(".mes:first").attr("id")) + 1;
$(".mes-all").prepend('<div class="mes" id="' + id + '">test' + id + '</div>');
var divHiehgt = $('.mes-all')[0].scrollHeight;
$('.mes-all').scrollTop(divHiehgt);
});
.mes-all {
overflow: auto;
height: 300px;
border: 1px solid;
}
.mes {
height: 30px;
padding: 5px;
border: 1px solid red;
text-align: center;
vertical-align: middle;
padding-top: 15px;
background: #ebebeb;
}
button {
width: 100%;
height: 50px;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="mes-all">
<div class="mes" id="1">test1</div>
</div>
<button id="add">Send</button>
Or jsFiddle:
https://jsfiddle.net/j8jaz10k/1/

jQuery | detach(), append() scope

I have a main-div and two divs with the class of container. The div with the class of container has a child div with a class of content with different contents. I'd like for the user to click on their choice of containers and transport its content to main-div. Then when the user clicks on the main-div, I'd like to transport that content back to its original div.
I'm not sure how to detach the content from main-div once it's been passed and reinsert it back into its original parent. I would appreciate any help.
I can't use IDs. I can only uses classes.
HTML
<div class="main-div">
</div>
<div class="container">
<div class="contents">
A
</div>
</div>
<div class="container">
<div class="contents">
B
</div>
</div>
CSS
.main-div {
width: 100wv;
height: 200px;
background: yellow;
cursor: pointer;
}
.container {
width: 40vw;
height: 150px;
border: 2px solid purple;
display: inline-block;
}
.contents {
width: 50px;
height: 50px;
background: green;
}
JS
$('.container').click(function() {
var child = $(this).children();
console.log('child ' + child);
$('.main-div').append(child);
});
$('.main-div').click(function(child) {
console.log('child ' + child);
$('.main-div').detach(child);
});
FIDDLE
Set ids for the containers
<div class="main-div">
</div>
<div class="container" id="container1">
<div class="contents">
A
</div>
</div>
<div class="container" id="container2">
<div class="contents">
B
</div>
</div>
And set a data attribute for the children on click to identify the parent element.
$(function() {
$('.container').click(function() {
var child = $(this).children();
child.attr("data-parentcontainer", this.id);
$('.main-div').append(child);
});
$('.main-div').click(function(child) {
var child = $(this).children();
child.appendTo($("#" + child.data("parentcontainer")));
});
});
JSFIDDLE
Use this JS snippet and let me know if it helps
$('.container').click(function() {
console.log('foo');
var child = $(this).html();
console.log(child);
$('.main-div').append(child);
});
$('.main-div').click(function() {
console.log('foo');
var main = $(this).html();
if(main.length != 0) {
$('.main-div').empty();
}
else
console.log('Main div is empty');
});
External DEMO
You can use .detach() and .appendTo(), but along with that you have to keep some identification to know from that .contents div was picked up. So I am making use of data-address attribute for the parent of picked .contents so as to attach it back there. See inline comments for detailed explanation on what will happen with the code.
$('.main-div').on('click', function(e) {
var elem = $(e.target); //capture click event on .main-div
if (elem.hasClass('contents')) { //check if click was on .contents div
var text = elem.text().trim(); //if yes then get its text
elem.detach();//detach the element
elem.appendTo($('div[data-address=' + text + ']')); //attach it based on attribute selector of jquery
}
});
$('.contents').on('click', function() {
var elem = $(this);//get the element
elem.closest('.container').attr('data-address', elem.text().trim())
//add or update data-address attribute of its closest parent i.e. .container
elem.detach();//detach the element
elem.appendTo($('.main-div')); //append it to .main-div
})
.main-div {
width: 200px;
height: 200px;
background: yellow;
cursor: pointer;
}
.container {
width: 150px;
height: 150px;
border: 2px solid purple;
}
.contents {
width: 50px;
height: 50px;
background: green;
position: relative;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main-div">
</div>
<div class="container">
<div class="contents">
A
</div>
</div>
<div class="container">
<div class="contents">
B
</div>
</div>
You need to add some uniqueness on the div that have class container, you can add a class or id so that when reinserting back to the original div we can identify the div.
i don't think is there other solution to reinsert back to the original div, whenever we identify both div uniquely.
please change your html structure so that we can manipulate through jquery.

Progress bar with slide ability

I am new to JavaScript/CSS (basically the whole world of web dev) and I am really struggling to create the following widget. I created a picture of what I want to make to make it more clear.
The Play/Pause and Stop button are ready. Loop checkbox is no problem. But the progress bar is painful. The two markers are supposed to mark the point from where the file would start playing and where it would stop. The progress bar is also supposed to be click-able, so if I want to access a certain point in time, then its possible.
What I tried so far
jQuery UI slider: For a sliding progress bar and use that draggable slider to access a certain point in audio file. Works fine. But no markers and looks really ugly. Don't how to change it.
<progress> tag: not very flexible. Marker? Clicking?
<div> tag: there seems to be no way to get the point where I clicked.
So, what do you guys recommend? How should I proceed?
Canvas Alternative
You might want to use a canvas and draw your own progress bar element within it.
Here are some canvas progress bar tutorials:
How to create a progress bar with HTML5
A progress bar using HTML5 canvas
Doing it with <progress>
To access the clicked position within a DOMElement, you can proceed with the click event's properties: clientX and clientY (MDN Source), like so:
HTML
<div class="marker" id="StartMarker">^</div>
<div class="marker" id="StopMarker">^</div>
<progress id="progress" value="0" min="0" max="100">0%</progress>
<form id="choice">
<button id="marker1">Beginning marker</button>
<button id="marker2">Ending marker</button>
<input type="hidden" id="markerValue" value="0" />
</form>
JavaScript (not optimized)
document.getElementById('progress').onclick = function (event, element) {
/* Math.floor((event.offsetX / this.offsetWidth) * 100) */
var newProgress = event.offsetX;
document.getElementById('choice').style.display = "block";
document.getElementById('markerValue').setAttribute('value', newProgress);
document.getElementById('marker1').onclick = function (event) {
event.preventDefault();
var newProgress = document.getElementById('markerValue').value;
var progressBar = document.getElementById('progress');
var startMarker = document.getElementById('StartMarker');
var stopMarker = document.getElementById('StopMarker');
var marker = startMarker;
marker.style.display = "block";
startMarker.style.display = "block";
startMarker.offsetTop = (progressBar.offsetTop + progressBar.offsetHeight + 2) + "px";
startMarker.style.left = newProgress + "px";
};
document.getElementById('marker2').onclick = function (event) {
event.preventDefault();
var newProgress = document.getElementById('markerValue').value;
var progressBar = document.getElementById('progress');
var startMarker = document.getElementById('StartMarker');
var stopMarker = document.getElementById('StopMarker');
stopMarker.style.display = "block";
stopMarker.offsetTop = (progressBar.offsetTop + progressBar.offsetHeight + 2) + "px";
stopMarker.style.left = newProgress + "px";
};
};
CSS
.marker {
position:absolute;
top:24px;
left:9px;
display:none;
z-index:8;
font-weight:bold;
text-align:center;
}
#StartMarker {
color: #CF0;
}
#StopMarker {
color:#F00;
}
#choice {
display:none;
}
progress {
display: inline-block;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 300px;
height: 20px;
padding: 3px 3px 2px 3px;
background: #333;
background: -webkit-linear-gradient(#2d2d2d, #444);
background: -moz-linear-gradient(#2d2d2d, #444);
background: -o-linear-gradient(#2d2d2d, #444);
background: linear-gradient(#2d2d2d, #444);
border: 1px solid rgba(0, 0, 0, .5);
border-radius: 15px;
box-shadow: 0 1px 0 rgba(255, 255, 255, .2);
}
Live Demo
Using simple blocks for that is possible. Your layout would look like this (simplified):
HTML
<div class="progressbar">
<div class="bar">
<div class="progress" style="width: 30%;">
</div>
</div>
<div class="markers">
<div class="right" style="width: 70%;">
<div class="marker">
</div>
<div class="left" style="width: 20%;">
<div class="marker">
</div>
</div>
</div>
</div>
</div>
SCSS
.progressbar {
width: 20em;
background: grey;
.bar {
height: 2em;
.progress {
background: blue;
height: 100%;
}
}
.markers {
height: 1em;
background: white;
.right {
height: 100%;
background: red;
.marker {
width: 1em;
height: 100%;
background: green;
position: relative;
float: right;
}
.left {
background: white;
height: 100%;
}
}
}
}
The operations can be quite difficult
jQuery
$('.bar').click(function(e){
$(this).find('.progress').css('width', (e.offsetX / this.offsetWidth)*100+'%');
});
will set the Progressbar properly on clicks.
For the markers though you will need mousedown, mousemove, mouseleave events, since you got 2 of them.
Example
http://jsfiddle.net/JXauW/

Categories

Resources