I have a web app with a number of textareas and the ability to add more if you wish.
When you shift focus from one textarea to another, the one in focus animates to a larger size, and the rest shrink down.
When the page loads it handles the animation perfectly for the initial four boxes in the html file, but when you click on the button to add more textareas the animation fails to accomodate these new elements... that is, unless you place the initial queries in a function, and call that function from the addelement function tied to the button.
But!, when you do this it queries as many times as you add a new element. So, if you quickly add, say 10, new textareas, the next time you lay focus on any textarea the query runs 10 times.
Is the issue in my design, or jQueries implementation? If the former, how better can I design it, if it is the latter, how can I work around it?
I've tried to chop the code down to the relevant bits... I've tried everything from focus and blur, to keypresses, the latest is on click.
html::
<html>
<head>
<link rel="stylesheet" type="text/css" href="./sty/sty.css" />
<script src="./jquery.js"></script>
<script>
$().ready(function() {
var $scrollingDiv = $("#scrollingDiv");
$(window).scroll(function(){
$scrollingDiv
.stop()
//.animate({"marginTop": ($(window).scrollTop() + 30) + "px"}, "slow" );
.animate({"marginTop": ($(window).scrollTop() + 30) + "px"}, "fast" );
});
});
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>boxdforstacks</title>
</head>
<body>
<div class="grid">
<div class="col-left" id="left">
<div class="module" id="scrollingDiv">
<input type="button" value="add" onclick="addele()" />
<input type="button" value="rem" onclick="remele()" />
<p class="display">The value of the text input is: </p>
</div>
</div> <!--div class="col-left"-->
<div class="col-midd">
<div class="module" id="top">
<p>boxa</p>
<textarea class="tecksd" placeholder="begin typing here..." id="boxa" ></textarea>
<p>boxb</p>
<textarea class="tecksd" placeholder="begin typing here..." id="boxb"></textarea>
<p>boxc</p>
<textarea class="tecksd" placeholder="begin typing here..." id="boxc"></textarea>
<p>boxd</p>
<textarea class="tecksd" placeholder="begin typing here..." id="boxd"></textarea>
</div>
</div> <!--div class="col-midd"-->
</div> <!--div class="grid"-->
</body>
</html>
<script type="text/javascript" src="boxd.js"></script>
js:
function onit(){
$('textarea').on('keyup change', function() {
$('p.display').text('The value of the text input is: ' + $(this).val());
});
}
$('textarea').on("click",function(){
//alert(this.id.substring(0,3));
if ( this.id.substring(0,3) == 'box' ){
$('textarea').animate({ height: "51" }, 1000);
$(this).animate({ height: "409" }, 1000);
} else {
$('textarea').animate({ height: "51" }, 1000);
}
}
);
var boxfoc="";
var olebox="";
var numb = 0;
onit();
function addele() {
var tops = document.getElementById('top');
var num = numb + 1;
var romu = romanise(num);
var newbox = document.createElement('textarea');
var newboxid = 'box'+num;
newbox.setAttribute('id',newboxid);
newbox.setAttribute('class','tecksd');
newbox.setAttribute('placeholder','('+romu+')');
tops.appendChild(newbox);
numb = num;
onit();
} //addele(), add element
function remele(){
var tops = document.getElementById('top');
var boxdone = document.getElementById(boxfoc);
tops.removeChild(boxdone);
} // remele(), remove element
function romanise (num) {
if (!+num)
return false;
var digits = String(+num).split(""),
key = ["","c","cc","ccc","cd","d","dc","dcc","dccc","cm",
"","x","xx","xxx","xl","l","lx","lxx","lxxx","xc",
"","i","ii","iii","iv","v","vi","vii","viii","ix"],
roman = "",
i = 3;
while (i--)
roman = (key[+digits.pop() + (i * 10)] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
} // romanise(), turn numbers into roman numerals
css :
.tecksd {
width: 97%;
height: 51;
resize: none;
outline: none;
border: none;
font-family: "Lucida Console", Monaco, monospace;
font-weight: 100;
font-size: 70%;
background: white;
/* box-shadow: 1px 2px 7px 1px #0044FF;*/
}
.tecksded {
width: 97%;
resize: none;
outline: none;
border: none;
overflow: auto;
position: relative;
font-family: "Lucida Console", Monaco, monospace;
font-weight: 100;
font-size: 70%;
background: white;
/* box-shadow: 1px 2px 7px #FFDD00;*/
}
/*#postcomp {
width: 500px;
}*/
* {
#include box-sizing(border-box);
}
$pad: 20px;
.grid {
background: white;
margin: 0 0 $pad 0;
&:after {
/* Or #extend clearfix */
content: "";
display: table;
clear: both;
}
}
[class*='col-'] {
float: left;
padding-right: $pad;
.grid &:last-of-type {
padding-right: 0;
}
}
.col-left {
width: 13%;
}
.col-midd {
width: 43%;
}
.col-rght {
width: 43%;
}
.module {
padding: $pad;
}
/* Opt-in outside padding */
.grid-pad {
padding: $pad 0 $pad $pad;
[class*='col-']:last-of-type {
padding-right: $pad;
}
}
body {
padding: 10px 50px 200px;
background: #FFFFFF;
background-image: url('./backgrid.png');
}
h1 {
color: black;
font-size: 11px;
font-family: "Lucida Console", Monaco, monospace;
font-weight: 100;
}
p {
color: white;
font-size: 11px;
font-family: "Lucida Console", Monaco, monospace;
font-weight: 100;
}
You should use the following:
// New way (jQuery 1.7+) - .on(events, selector, handler)
$(document).on("click", "textarea", function () {
event.preventDefault();
alert('testlink');
});
Since the textarea is added dynamically, you need to use event delegation to register the event handler.
Try
$(document).on('click', 'textarea', function() {
// do something
});
The issue is you are binding the textareas only on the page load. I made a JSFiddle with working code: http://jsfiddle.net/VpABC/
Here's what I changed:
I wrapped:
$('textarea').on("click", function () {
//alert(this.id.substring(0,3));
if (this.id.substring(0, 3) == 'box') {
$('textarea').animate({
height: "51"
}, 1000);
$(this).animate({
height: "409"
}, 1000);
} else {
$('textarea').animate({
height: "51"
}, 1000);
}
});
in a function so it looked like this:
function bindTextAreas() {
$('textarea').unbind("click");
$('textarea').on("click", function () {
//alert(this.id.substring(0,3));
if (this.id.substring(0, 3) == 'box') {
$('textarea').animate({
height: "51"
}, 1000);
$(this).animate({
height: "409"
}, 1000);
} else {
$('textarea').animate({
height: "51"
}, 1000);
}
});
}
bindTextAreas();
What this does is it allows you to call this function, bindTextAreas, whenever you create a new textarea. This will unbind all the current events than rebind them. This will make it so your new textarea is has the click handler setup.
An place where this function is called is in the addele function like this:
function addele() {
var tops = document.getElementById('top');
var num = numb + 1;
var romu = romanise(num);
var newbox = document.createElement('textarea');
var newboxid = 'box' + num;
newbox.setAttribute('id', newboxid);
newbox.setAttribute('class', 'tecksd');
newbox.setAttribute('placeholder', '(' + romu + ')');
tops.appendChild(newbox);
numb = num;
onit();
bindTextAreas();
} //addele(), add element
Notice the bindTextAreas(); line near the bottom. This reloads all the click handlers.
Related
I have some quantity inputs. I want to collect the data in "inputs" and show them in "#yolcudropdown". But I just can't pull the data. Inputs must be disabled. There should be no manual entry. I did something at the bottom of the "javascript" section. But I couldn't run it.
(function( $ ) {
$.fn.number = function(customOptions) {
var options = {
'containerClass' : 'number-style',
'minus' : 'number-minus',
'plus' : 'number-plus',
'containerTag' : 'div',
'btnTag' : 'span'
};
options = $.extend(true, options, customOptions);
var input = this;
input.wrap('<' + options.containerTag + ' class="' + options.containerClass + '">');
var wrapper = input.parent();
wrapper.prepend('<' + options.btnTag + ' class="' + options.minus + '"></' + options.btnTag + '>');
var minus = wrapper.find('.' + options.minus);
wrapper.append('<' + options.btnTag + ' class="' + options.plus + '"></' + options.btnTag + '>');
var plus = wrapper.find('.' + options.plus);
var min = input.attr('min');
var max = input.attr('max');
if(input.attr('step')){
var step = +input.attr('step');
} else {
var step = 1;
}
if(+input.val() <= +min){
minus.addClass('disabled');
}
if(+input.val() >= +max){
plus.addClass('disabled');
}
minus.click(function () {
var input = $(this).parent().find('input');
var value = input.val();
if(+value > +min){
input.val(+value - step);
if(+input.val() === +min){
input.prev('.' + options.minus).addClass('disabled');
}
if(input.next('.' + options.plus).hasClass('disabled')){
input.next('.' + options.plus).removeClass('disabled')
}
} else if(!min){
input.val(+value - step);
}
});
plus.click(function () {
var input = $(this).parent().find('input');
var value = input.val();
if(+value < +max){
input.val(+value + step);
if(+input.val() === +max){
input.next('.' + options.plus).addClass('disabled');
}
if(input.prev('.' + options.minus).hasClass('disabled')){
input.prev('.' + options.minus).removeClass('disabled')
}
} else if(!max){
input.val(+value + step);
}
});
};
})(jQuery);
$('.quntity-input').each(function () {
$(this).number();
});
/* THIS IS IMPORTANT */
$(document).ready(function() {
$(document).on('change', '.btw', function() {
$('#yolcudropdown').text($(this).val());
});
});
.number-style {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-ms-flex-pack: start !important;
justify-content: flex-start !important;
-ms-flex-align: center !important;
align-items: center !important;
}
.number-style .number-minus,
.number-style .number-plus {
height: 28px;
background: #ffffff;
border: 2px solid #e2e2e2 !important;
width: 28px;
-webkit-border-radius: 100%;
-moz-border-radius: 100%;
-ms-border-radius: 100%;
border-radius: 100%;
line-height: 23px;
font-size: 19px;
font-weight: 700;
text-align: Center;
border: none;
display: block;
cursor: pointer;
}
.number-style .number-minus:active,
.number-style .number-plus:active {
background: #e2e2e2;
}
.number-style .number-minus {
line-height: 20px;
}
.number-style .number-minus::after {
content: "-";
font-size: 10px;
}
.number-style .number-plus {
line-height: 18px;
}
.number-style .number-plus::after {
content: "+";
font-size: 10px;
}
.number-style .quntity-input {
width: 28px;
background: #e00f23;
-webkit-border-radius: 100%;
-moz-border-radius: 100%;
-ms-border-radius: 100%;
border-radius: 100%;
line-height: 21px;
font-size: 14px;
color: #ffffff;
font-weight: 700;
text-align: Center;
margin: 0 5px;
display: block;
cursor: pointer;
text-align: center;
border: none;
height: 28px;
font-weight: 600;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="quntity-input btw" type="text" value="0" step="1" min="0" max="10">
<input class="quntity-input btw" type="text" value="0" step="1" min="0" max="10">
<div id="yolcudropdown">İnput quantity show this div</div>
"class" names of "input" elements are the same. I need to collect inputs with the same value and display them in the div instantly
HTMLInputElement
Input type "text" has no min max or step attributes, so your HTML is absolutely invalid. Try not to write It-works, I'm a framework -code. Respect the W3C standards.
Use type="number" (CSS pseudos can help you remove default spinner arrows from such elements)
Also step could be floats. Respect that and use parseFloat() in JS
CSS Flex to align stuff
Seems you know about CSS-flex, use it! Height, therefore- line-height... 19? 20? 23px? No. Just use flex.
CSS !important
!important is sign of poor coding style and should be left to Bootstrap only. Or to hopefully override Bootstrap styles - or in that cases when developers actually know what they are doing.
jQuery Plugins
jQuery plugins, I suggest to read the DOCS and get a deeper knowledge on how plugins work. Almost every jQuery method is a plugin. .hide() , .addClass()... I won't count them all. Plugins are chainable .removeClass("foo").stop().fadeTo(1), and so should be your .number() plugin.
To achieve chain-ability you simply return the bound this. PS: that's not jQuery... that's how JS works.
jQuery Plugins are not meant to be called inside a $.each() loop. $() is already a collection of DOM Nodes wrapped in a jQuery Object. No need to .each(). Same like: you would rather use $('a').css({color:'blue'}) instead of $('a').each(){ $(this).css({color: 'blue'}); });. Same effect, less code. Plugins.
jQuery DOM ready
jQuery(function($) { }); // DOM ready and $ alias in scope
Or if you don't care about ±IE, or you use ES6 syntax and a toolchain like Babel than: jQuery($ => { }) will suffice.
jQuery $ Object Constructor
jQuery allows you to define an HTMLElement that will eventually become a new DOM element wrapped with all the jQuery powers, Methods. Meaning that, if instead of passing a selector, you pass a more complex Tag-alike string (say: $("<span/>", {}); - jQuery will create an inMemory SPAN element and allow you to use the second parameter {} for most of the available jQuery Methods for that $Element. Let's use this!
jQuery plugin callbacks
If you want to provide a callback after a user changes the input value, provide a callback method. Don't force a programmer to write new spaghetti code, stick to the scope of your available Plugin internal Methods.
Sum Elements values
To sum Elements values you can use Array.prototype.reduce, just make sure to use an initialValue to prevent possible TypeErrors.
Example
Finally, here's the simplified CSS and improved JS:
(function($) {
$.fn.number = function(customOptions) {
const options = $.extend(true, {
containerTag: "div",
containerClass: "number-style",
minusClass: "number-minus", // consistency in wording!
minusText: "-", // Give power to the user!
plusClass: "number-plus",
plusText: "+",
btnTag: "button",
onChange() {}, // Provide a nifty callback!
}, customOptions);
this.each(function() { // Use .each() here!
const $input = $(this);
let val = parseFloat($input.value || 0); // floats!
const min = parseFloat($input.attr("min"));
const max = parseFloat($input.attr("max"));
const step = parseFloat($input.is("[step]") ? $input.attr("step") : 1);
const handleStyles = () => {
$minus.toggleClass('disabled', val <= min);
$plus.toggleClass('disabled', val >= max);
};
const change = () => {
val = Math.max(min, Math.min(max, val)); // Keep val in range.
$input.val(val); // Update input value
handleStyles(); // Update styles
options.onChange.call($input[0], val); // Trigger a public callback
}
const decrement = () => {
val -= step;
change();
};
const increment = () => {
val += step;
change();
};
const $minus = $(`<${options.btnTag}>`, {
type: "button",
title: "Decrement",
class: options.minusClass,
text: options.minusText,
on: {
click: decrement
}
});
const $plus = $(`<${options.btnTag}>`, {
class: options.plusClass,
title: "Increment",
text: options.plusText,
on: {
click: increment
}
});
const $wrapper = $(`<${options.containerTag}>`, {
class: options.containerClass,
});
$input.after($wrapper);
$wrapper.append($minus, $input.detach(), $plus); // Append all
handleStyles(); // handle initial styles
});
return this; // make your plugin chainable!
};
})(jQuery);
jQuery(function($) { // DOM ready and $ alias in scope
const $quantityInp = $('.quantity-input'); // Cache your elements!
const $dropdown = $('#yolcudropdown'); // Cache your elements!
$quantityInp.number({
onChange(val) { // our custom onChange callback!
const tot = $quantityInp.get().reduce((acc, el) => {
acc += parseFloat(el.value);
return acc;
}, 0);
$dropdown.text(tot);
}
});
});
/* QuickReset */ * { margin:0; box-sizing:border-box; }
.number-style input::-webkit-outer-spin-button,
.number-style input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
.number-style {
display: flex;
}
.number-style > * {
height: 2em;
min-width: 2em;
border-radius: 2em;
display: flex; /* Use flex. */
justify-content: center;
text-align: center;
border: 0;
background: #ddd;
}
.number-style button {
background: #fff;
box-shadow: inset 0 0 0 2px #ccc;
cursor: pointer;
user-select: none;
/* no highlight, please! */
}
.number-style button:active {
background: #0bf;
}
.number-style input {
background: #e00f23;
color: #fff;
margin: 0 5px;
}
.number-style .disabled {
opacity: 0.2;
cursor: default;
}
/* Custom overrides: */
.number-style>* {
width: 2em;
/* just for roundness */
}
<input class="quantity-input" type="number" value="0" step="1" min="0" max="10">
<input class="quantity-input" type="number" value="0" step="1" min="0" max="10">
<div id="yolcudropdown">0</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
Additional reading:
HTMLInputElement
Math/min
Math/max
jQuery plugin-creation
$ new-elements
Array/reduce
jQuery.toggleClass()
And PS: it's "quantity", not "quntity"
I have this code which uses a typewriter animation to replace the placeholder text in a search bar:
https://codepen.io/anon/pen/vQmRjM
////////////////////////////
// Twitter: #mikedevelops
////////////////////////////
// your custome placeholder goes here!
var typetext = ["Text 1", "Text 2", "Text 3"];
var typetext = typetext[Math.floor(Math.random() * typetext.length)];
var searchBar = $('#search');
// placeholder loop counter
var phCount = 0;
// function to return random number between
// with min/max range
function randDelay(min, max) {
return Math.floor(Math.random() * (max-min+1)+min);
}
// function to print placeholder text in a
// 'typing' effect
function printLetter(string, el) {
// split string into character separated array
var arr = string.split(''),
input = el,
// store full placeholder
origString = string,
// get current placeholder value
curPlace = $(input).attr("placeholder"),
// append next letter to current placeholder
placeholder = curPlace + arr[phCount];
setTimeout(function(){
// print placeholder text
$(input).attr("placeholder", placeholder);
// increase loop count
phCount++;
// run loop until placeholder is fully printed
if (phCount < arr.length) {
printLetter(origString, input);
}
// use random speed to simulate
// 'human' typing
}, randDelay(50, 90));
}
// function to init animation
function placeholder() {
$(searchBar).attr("placeholder", "");
printLetter(typetext, searchBar);
}
window.setInterval(function(){
placeholder();
}, 3000);
I added a setInterval to loop through the different placeholder text every few seconds, however after the first iteration it just starts showing undefined.
Any ideas where I have went wrong?
The issue is you are trying to access the array item from an index which does not really exist. You have to reset the variable phCount in each call of setInterval():
window.setInterval(function(){
phCount = 0; // reset here
placeholder();
}, 3000);
Code Example:
////////////////////////////
// Twitter: #mikedevelops
////////////////////////////
// your custome placeholder goes here!
var typetext = ["Text 1", "Text 2", "Text 3"];
var typetext = typetext[Math.floor(Math.random() * typetext.length)];
var searchBar = $('#search');
// placeholder loop counter
var phCount = 0;
// function to return random number between
// with min/max range
function randDelay(min, max) {
return Math.floor(Math.random() * (max-min+1)+min);
}
// function to print placeholder text in a
// 'typing' effect
function printLetter(string, el) {
// split string into character seperated array
var arr = string.split(''),
input = el,
// store full placeholder
origString = string,
// get current placeholder value
curPlace = $(input).attr("placeholder"),
// append next letter to current placeholder
placeholder = curPlace + arr[phCount];
//console.log(curPlace + '::' + arr[phCount] + '::' + phCount)
setTimeout(function(){
// print placeholder text
$(input).attr("placeholder", placeholder);
// increase loop count
phCount++;
// run loop until placeholder is fully printed
if (phCount < arr.length) {
printLetter(origString, input);
}
// use random speed to simulate
// 'human' typing
}, randDelay(50, 90));
}
// function to init animation
function placeholder() {
$(searchBar).attr("placeholder", "");
printLetter(typetext, searchBar);
}
window.setInterval(function(){
phCount = 0;
placeholder();
}, 3000);
#import url(https://fonts.googleapis.com/css?family=PT+Sans:400,700);
html {
background: linear-gradient(90deg, #00c6ff 10%, #0072ff 90%);
font-family: 'PT Sans', Helvetica, Arial, sans-serif;
padding-top: 50px;
}
h1 {
text-shadow: 1px 1px 10px rgba(black, .5);
}
h1, h2 {
text-align: center;
color: white;
font-size: 2.5em;
line-height: 1.3em;
font-weight: 300;
}
h2 {
margin-top: 100px;
font-size: 1.3em;
font-style: italic;
font-weight: 100;
}
.body {
width: 100%;
height: 250px;
box-sizing: border-box;
}
input {
box-sizing: border-box;
font-size: 13px;
vertical-align: top;
}
.wrapper {
text-align: center;
position: relative;
height: 80px;
font-size: 0;
top: 50%;
transform: translateY(-50%);
}
.search {
padding: 0 30px;
font-size: 18px;
width: 60%;
max-width: 400px;
height: 80px;
border: 1px solid darken(white, 30%);
border-radius: 20px 0 0 20px;
}
.submit {
cursor: pointer;
border: none;
background: url('http://thesuiteworld.com/wp-admin/maint/search-icon-white-png-540.png') no-repeat center center, #1E1E20;
background-size: 35px 35px;
border-radius: 0 20px 20px 0;
padding: 15px 25px;
display: inline-block;
width: 150px;
height: 80px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="body">
<h1>Placeholder text with<br/> typing effect</h1>
<div class="wrapper">
<input class="search" type="text" id="search" />
<input class="submit" type="submit" value=" " />
</div>
</div>
<h2>Click search to reset</h2>
to remove undefined, You need to reset the char length loop setting
update following code to reset the string length to 0
if (phCount arr.length) {
printLetter(origString, input);
}
else {
phCount = 0;
}
and to display next string use following code
var wordcount =0;
window.setInterval(function(){
if (wordcount >= typetext.length) {
wordcount = 0;
}
placeholder(wordcount);
wordcount++;
}, 3000);
This will loop the array of string. This should work.
I think you have to reset the phCount to 0 when you reset the placeholder ($(searchBar).attr("placeholder", "")).
All I'm trying to do is something like this mechanism:
Here is what I've tried so far:
$(document).ready(function(){
$('a').bind('mouseenter', function() {
var self = $(this);
this.iid = setInterval(function() {
var tag_name = self.text(),
top = self.position().top + self.outerHeight(true),
left = self.position().left;
self.append("<div class='tag_info'>Some explanations about"+tag_name+"</div>")
$(".tag_info").css({top: top + "px", left: left + "px"}).fadeIn(200);
}, 525);
}).bind('mouseleave', function(){
this.iid && clearInterval(this.iid);
});
});
body{
padding: 20px;
}
a {
color: #3e6d8e !important;
background-color: #E1ECF4;
padding: 2px 5px;
}
.tag_info{
position: reletive;
width: 130px;
height: 30px;
display:none;
background-color: black;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a>tag1</a>
<a>tag2</a>
As you see, it will be repeated every time. How can I execute it once per hover? And why the position doesn't apply?
Also is what I'm doing a right algorithm for such thing?
Thank you.
I am not sure why you are using setInterval but I think this should work. I removed setInterval and everytime the mouseenter event occurs we can append <div class='tag_info'> and every time mouseleave event occurs we can remove the the appended div.
$(document).ready(function(){
$('#test').bind('mouseenter', function() {
var self = $(this);
var tag_name = self.text(),
top = self.position().top + self.outerHeight(true),
left = self.position().left;
self.append("<div class='tag_info'>Some explanations about"+tag_name+"</div>")
$(".tag_info").css({top: top + "px", left: left + "px"}).fadeIn(200);
}).bind('mouseleave', function(){
$(this).children('.tag_info').remove();
});
});
body{
padding: 20px;
}
a {
color: #3e6d8e !important;
background-color: #E1ECF4;
padding: 2px 5px;
}
.tag_info{
position: reletive;
width: 130px;
height: 30px;
display:none;
background-color: black;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a id="test">tag1</a>
Like Dij said:
What you're doing:
setInterval - (repeats your function every 525ms)
What you want:
setTimeout - (executes your function once after 525ms delay)
Read more:
setInterval https://www.w3schools.com/jsref/met_win_setinterval.asp
setTimeout https://www.w3schools.com/jsref/met_win_settimeout.asp
My issue is that when i append 2 divs white jQuery, there names are:
This is div 1
This is div 2
But when i remove the first div (This is div 1)
and append another div
it adds one more div whit name (This is div 2):
This is div 2
This is div 2
The reason is because the name of the div counts the total amout of divs... Is there any other way to number all divs so they will always be like this:
This is div 1
This is div 2
This is div 3
Even if i the divs are:
This is div 1
This is div 6
This is div 12
I want them always to be 1,2,3
jQuery code:
$('#add_item').click(function() {
//div count
var countDivs = $("div").length;
//append content
var removeBtn = ('<a class="removeBtn">x</a>')
var h2 = ('<h2>This is div '+countDivs+'</h2>')
var appendContent = ('<div>'+h2+removeBtn+'</div>')
$('#accordion').append(appendContent);
});
//remove button
$(document).on('click', '.removeBtn', function() {
$(this).parent('div').andSelf().remove();
return false;
});
JSFIDDLE
I think you'll have to edit contents of the divs each time a div is removed.
Let's say you have an element and you want to add divs to it.
You will add like you are right now and when you remove you edit all other divs.
The code would be something like this
$('#add_item').click(function() {
var countDivs = $("div").length;
var removeBtn = ('<a class="removeBtn">x</a>')
var h2 = ('<h2>This is div '+countDivs+'</h2>')
var appendContent = ('<div class="appDiv">'+h2+removeBtn+'</div>')
$('#accordion').append(appendContent);
});
$(document).on('click', '.removeBtn', function() {
$(this).parent('div').andSelf().remove();
$('.appDiv').each(function(index,el){
$(el).find('h2').text('This is div '+(index+1));
});
return false;
});
here is the Fiddle
Hope this helps :)
Write a function to renaming the divs and call it after append/remove.
function reArrange() {
$("#accordion > div").each(function(i) {
$(this).find("h2").text("This is div" + (i + 1))
});
}
Fiddle
When an item is removed, change the title of all the elements after it.
$('#add_item').click(function() {
var countDivs = $("#accordion div").length + 1;
var removeBtn = ('<a class="removeBtn">x</a>')
var h2 = ('<h2>This is div <span>' + countDivs + '</span></h2>')
var appendContent = ('<div>' + h2 + removeBtn + '</div>')
$('#accordion').append(appendContent);
});
$(document).on('click', '.removeBtn', function() {
var $div = $(this).parent();
$div.nextAll('div').find('span').html(function(i, html) {
return --html
});
$div.remove();
return false;
});
div {
position: relative;
}
#accordion {
margin-left: 60px;
padding: 10px;
background: #ddd;
}
#add_item {
position: absolute;
top: 20px;
left: 20px;
font-size: 20px;
padding: 5px 10px;
background: black;
color: white;
cursor: pointer;
}
.removeBtn {
font-size: 20px;
padding: 2px 10px 5px;
background: black;
color: white;
cursor: pointer;
position: absolute;
top: 0px;
font-family: verdana;
border-radius: 100%;
left: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="accordion">
</div>
<a id="add_item">+</a>
you should use a global variable like "count":
var count=1;
$('#add_item').click(function() {
//div count
//var countDivs = $("div").length;
var countDivs =count;
//append content
var removeBtn = ('<a class="removeBtn">x</a>')
var h2 = ('<h2>This is div '+countDivs+'</h2>')
var appendContent = ('<div>'+h2+removeBtn+'</div>')
$('#accordion').append(appendContent);
count++;
});
//remove button
$(document).on('click', '.removeBtn', function() {
$(this).parent('div').andSelf().remove();
return false;
});
The easiest update would be to trigger a recount (or other named-event) and, upon addition or removal of an element – by clicking either the #add_item or .removeBtn – call that function using the on() method to listen for that event.
In the below code we bind the event-listener to the #accordion element, as the closest ancestor present in the DOM on page load:
$('#add_item').click(function() {
var removeBtn = ('<a class="removeBtn">x</a>');
var h2 = ('<h2></h2>');
var appendContent = ('<div>'+h2+removeBtn+'</div>');
$('#accordion').append(appendContent).trigger('recount');
});
$(document).on('click', '.removeBtn', function() {
$(this).parent('div').andSelf().remove();
// triggering the 'recount' event from the
// #accordion:
$('#accordion').trigger('recount');
return false;
});
// listening for the 'recount' event:
$('#accordion').on('recount', function(){
// looking within the #accordion for
// the <h2> elements (which contain the
// text to update), and using the text()
// method's anonymous function along with
// its i argument (the index of the current
// <h2> in the collection):
$(this).find('h2').text(function(i){
// returning the text string concatenated
// with the index plus 1 (to get a 1-based
// count, rather than JavaScript's 0-based):
return 'This is div ' + (i + 1);
});
});
$('#add_item').click(function() {
var removeBtn = ('<a class="removeBtn">x</a>');
var h2 = ('<h2></h2>');
var appendContent = ('<div>' + h2 + removeBtn + '</div>');
$('#accordion').append(appendContent).trigger('recount');
});
$(document).on('click', '.removeBtn', function() {
$(this).parent('div').andSelf().remove();
$('#accordion').trigger('recount');
return false;
});
$('#accordion').on('recount', function() {
$(this).find('h2').text(function(i) {
return 'This is div ' + (i + 1);
});
});
div {
position: relative;
}
#accordion {
margin-left: 60px;
padding: 10px;
background: #ddd;
}
#add_item {
position: absolute;
top: 20px;
left: 20px;
font-size: 20px;
padding: 5px 10px;
background: black;
color: white;
cursor: pointer;
}
.removeBtn {
font-size: 20px;
padding: 2px 10px 5px;
background: black;
color: white;
cursor: pointer;
position: absolute;
top: 0px;
font-family: verdana;
border-radius: 100%;
left: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="accordion">
</div>
<a id="add_item">+</a>
References:
on().
text().
trigger().
I am trying to have a simple continuous scrolling table to have a scrolling scoreboard. The general idea is that within a given div, the table will cycle through each team with their score in an upwards direction.
The marquee html tag is similar to what I want, though there are a few problems. First, many forums have advised against it. Second, even if I did use it I would need to fix the blank white space leading the first entry and following the last entry.
Ideally I would prefer not to use JS however it looks like my best option at this point.
A rough outline of the code is shown below, where I need the header to stay static but the table content to roll underneath and in line with the header. I have used the marquee html tag as a placeholder for an indication of how the scroll should interact with the content. This is the below rough code in jfiddle: here
<div>
<table><tr>
<td>Place</td>
<td>Team</td>
<td>Points</td>
</tr></table>
<marquee direction="up">
<table><tr>
<td>1</td>
<td>Team One</td>
<td>1000</td>
</tr>
<tr>
<td>2</td>
<td>Team Two</td>
<td>500</td>
</tr>
<tr>
<td>3</td>
<td>Team Three</td>
<td>250</td>
</tr></table>
</marquee>
</div>
All suggestions welcome.
Well this is the vscroller.js file plugin.. i had modified a bit to meet my needs
(function ($) {
$.fn.extend({
vscroller: function (options) {
var settings = $.extend({ speed: 2000, stay: 3000, newsfeed: '', cache: true }, options);
return this.each(function () {
var interval = null;
var mouseIn = false;
var totalElements;
var isScrolling = false;
var h;
var t;
var wrapper = $(this).addClass('news-wrapper');
if (settings.newsfeed == '') { alert('No XML file specified'); return; }
$.ajax({
url: settings.newsfeed,
type: 'GET',
dataType: 'xml',
cache: settings.cache,
success: function (xml) {
//if there are news headlines then build the html
var contentWrapper = $('<div/>').addClass('news-contents-wrapper');
var newsHeader = $('<div/>').addClass('news-header');
var newsContents = $('<div/>').addClass('news-contents');
wrapper.append(contentWrapper);
contentWrapper.append(newsHeader);
contentWrapper.append(newsContents);
newsHeader.html($(xml).find('newslist').attr('title'));
var i = 0;
totalElements = $(xml).find('news').length;
$(xml).find('news').each(function () {
var news = $('<div/>').addClass('news');
newsContents.append(news);
var description = $('<div/>').addClass('description');
news.append(description);
var url = $(this).attr('url');
var htext = $(this).find('headline').text();
description.append($('<span>').html("<img src='home/images/icons/bullet.png' /> <a style='color:#ffffff' href='" + url + "'>" + htext + "</a>"));
var newsText = $(this).find('detail').text();
if (newsText.length > 80) {
newsText = newsText.substr(0, 80) + "...";
}
description.append($('<div/>').addClass('detail').html(newsText));
});
h = parseFloat($('.news:eq(0)').outerHeight());
$('.news', wrapper).each(function () {
$(this).css({ top: i++ *20 });
});
t = (totalElements - 1) * h;
newsContents.mouseenter(function () {
mouseIn = true;
if (!isScrolling) {
$('.news').stop(true, false);
clearTimeout(interval);
}
});
newsContents.mouseleave(function () {
mouseIn = false;
interval = setTimeout(scroll, settings.stay);
});
interval = setTimeout(scroll, 1);
}
});
//$.get(settings.newsfeed, );
function scroll() {
if (!mouseIn && !isScrolling) {
isScrolling = true;
$('.news:eq(0)').stop(true, false).animate({ top: -50 }, settings.speed, function () {
clearTimeout(interval);
var current = $('.news:eq(0)').clone(true);
current.css({ top: 40 });
$('.news-contents').append(current);
$('.news:eq(0)').remove();
isScrolling = false;
interval = setTimeout(scroll, settings.stay);
});
$('.news:gt(0)').stop(true, false).animate({ top: '-=' + 20 }, settings.speed);
}
}
});
}
});
})(jQuery);
The corresponding CSS file
.news-wrapper
{
}
.news-wrapper .news-contents-wrapper
{
width: 200px;
margin: auto;
height: 20px;
}
.news-wrapper .news-contents
{
overflow: hidden;
position: relative;
z-index: 998;
height: 200px;
right:8px;
}
.news-wrapper .news
{
width: 100%;
height: 5px;
color: #6a6a6a;
position: absolute;
}
.news-wrapper .news-header
{
color: White;
height: 20px;
font-weight: bold;
font-size: 14px;
padding-top: 12px;
padding-left: 10px;
padding-bottom: 20px;
}
h1
{
color: White;
font-size: 14px;
}
.clear
{
clear: both;
}
.history
{
padding-top: 14px;
float: left;
width: 26%;
padding-left: 32px;
}
.description
{
float: left;
width: 64%;
padding: 4px;
}
.description .detail
{
font-size: 12px;
overflow: hidden;
color:#B1B1B1;
}
.elipses, .day, .month
{
display: block;
height: 10px;
}
.day, .month
{
padding-top: 6px;
}
h1 a, h1 a:active, h1 a:visited
{
text-decoration: none;
color:White;
}
h1 a:hover
{
text-decoration: underline;
color:White;
}
The xml file with the details that i needed to scroll
<?xml version="1.0" encoding="utf-8" ?>
<newslist title="Quick Links">
<news url="#" date="">
<headline>Details 1</headline>
</news>
<news url="#" date="">
<headline>Details 1</headline>
</news>
<news url="#" date="">
<headline>Details 1</headline>
</news>
</newslist>
My html file
<span class="news-wrapper" id="vscroller" style=" background:rgba(1,102,220,0.3);left: 20px;top:8px;position:relative;float: left; top:228px;height:95px;width: 150px; padding:0 2% 0 2%;">
</span>
and finally the js
<script type="text/javascript">
jQuery(document).ready(function () {
jQuery('#vscroller').vscroller({ newsfeed: 'home/js/news.xml' });
});
</script>