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 2 buttons with the same html structure on the same page. I have some js code to set the width depending on the text length of the button text. I need the js code to affect each individual button.
Please see the codepen here:
https://codepen.io/gianpiero-di-lullo/pen/gOYxoVr
I've inserted them in an each() loop
$(".LIbtnWrapper").each(function(){
var frontW = $(".LIbtnFront h1").width(),
frontH = $(".LIbtnFront h1").height(),
backW = $(".LIbtnBack h1").width(),
backH = $(".LIbtnBack h1").height();
if(frontW > backW) {
$(".LIbtnBack h1").width(frontW);
} else {
$(".LIbtnFront h1").width(backW);
}
})
I expect each button to set their own frontFace and backface width based on their text length and also behave independently on mouse events
The problem is there's no relation between your callback context and the way you're targeting the inner elements.
$(".LIbtnWrapper").each(function(){
var frontW = $(".LIbtnFront h1").width(),
The selector doesn't know that you mean the h1 in the element being iterated over - you're using a general selector, so it will just use the width of the first one it finds in the DOM matching the selector.
Instead, tightly couple your selectors to your callback context.
$(".LIbtnWrapper").each(function(){
var frontW = $(this).find("h1").width(), //<-- h1 inside current iterator element
You can use $(this) inside each to select the processing element and find() to select the child:
var a = $(".LIbtnWrapper");
$.each(a,function(){
var front = $(this).find(".LIbtnFront").find("h1");
var back = $(this).find(".LIbtnBack ").find("h1");
var frontW = front.width(),
frontH = front.height(),
backW = back.width(),
backH = back.height();
if(frontW > backW) {
back.width(frontW);
} else {
front.width(backW);
}
})
This is about context, and the element is each .LIbtnWrapper - the context. So you have to find the child element from that for the buttons.
More about context here https://stackoverflow.com/a/16422612/125981
$(".LIbtnWrapper").each(function(index, element) {
let bf = $(element).find(".LIbtnFront").find("h1");
let bb = $(element).find(".LIbtnBack").find("h1");
let frontW = bf.width(),
frontH = bf.height(),
backW = bb.width(),
backH = bb.height();
console.log(frontW, frontH, backW, backH);
if (frontW > backW) {
bb.width(frontW);
} else {
bf.width(backW);
}
});
TweenLite.set(".LIbtnWrapper", {
perspective: 1000
});
TweenLite.set(".LIbtn", {
transformStyle: "preserve-3d"
});
TweenLite.set(".LIbtnBack", {
rotationX: -90,
transformOrigin: "50% top",
y: "100%"
});
TweenLite.set([".LIbtnBack", ".LIbtnFront"], {
backfaceVisibility: "hidden"
});
$(".LIbtnWrapper").hover(
function() {
TweenLite.to($(this).find(".LIbtn"), .3, {
rotationX: 90,
ease: Power4.easeOut
});
},
function() {
TweenLite.to($(this).find(".LIbtn"), .5, {
rotationX: 0,
ease: Power4.easeOut
});
}
);
$(".LIbtnWrapper")
.mouseup(function() {
TweenLite.to($(this), .1, {
transformOrigin: "center",
scale: "1"
});
$(".LIbtnFace h1").css({
color: "#fff"
});
})
.mousedown(function() {
TweenLite.to($(this), .04, {
transformOrigin: "center",
scale: ".96"
});
$(".LIbtnFace h1").css({
color: "#00B1A7"
});
});
body {
background-color: black;
margin: 20px;
font-family: Arial, sans-serif;
padding: 20px 0px
}
.container {
width: 80%;
display: flex
}
.LIbtnWrapper {
position: relative;
float: left;
margin: auto;
cursor: pointer;
-webkit-font-smoothing: antialiased;
}
.LIbtnFace {
position: absolute;
overflow: hidden;
}
.LIbtnFront,
.LIbtnBack {
background-color: rgba(0 0 0 0);
}
.LIbtn-large {
font-size: 30px;
}
.LIbtn-medium {
font-size: 20px;
}
.LIbtn-small {
font-size: 10px;
}
.LIbtnFace h1 {
margin: auto;
padding: 15px;
border: solid 6px #fff;
color: white;
white-space: nowrap;
text-align: center;
}
.LIbtnFace.LIbtnBack h1 {
border-color: #00B1A7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<div class="container">
<div class="LIbtnWrapper">
<div class="LIbtn">
<div class="LIbtnFace LIbtnFront">
<h1 class="LIbtn-large">FRONT</h1>
</div>
<div class="LIbtnFace LIbtnBack">
<h1 class="LIbtn-large">THIS IS THE BACK</h1>
</div>
</div>
</div>
<div class="LIbtnWrapper">
<div class="LIbtn">
<div class="LIbtnFace LIbtnFront">
<h1 class="LIbtn-large">ANOTHER FRONT</h1>
</div>
<div class="LIbtnFace LIbtnBack">
<h1 class="LIbtn-large">BACK</h1>
</div>
</div>
</div>
</div>
jQuery's .each takes two arguments: index and element
$('.LIbtnWrapper').each(function(index, element){
// log the current element in this iteration
console.log('the current element: ', element);
// get child element with context of this/element
var thisH1 = $('.LIbtnFront h1', element);
console.log('the child h1 of the current element', thisH1);
});
This is the "jQuery" way of accessing the current element and its children as shown in the api examples.
I was just going through the web and found some cool text animations over here.
so I thought of taking a part of it and extending it.
As I know what to do first I went through the stack to find out whether any question will relate my idea and for my surprise, I had found one that explains what I need but that has not been answered correctly. I guess the main cause was because it was not explained correctly. so I will try my best to explain the idea.
Previously asked question
The Idea.
Let's suppose I have a heading tag that I need to animate. Them main Idea is not to break one same sentence into two instead if you write a heading or paragraph tag the whole thing should animate. I want to lift/reveal/raise the words from where they are in the image (from the line)
I have changed some of the existing code from the source I got. But the text is revealing/raising from the bottom of the whole block. Which I don't want. I want it to raise them from the line at bottom.
The Code:
// Wrap every letter in a span
$('.ml16').each(function() {
$(this).html($(this).text().replace(/([^\x00-\x80]|\w)/g, "<span class='letter'>$&</span>"));
});
anime.timeline({
loop: true
})
.add({
targets: '.ml16 .letter',
translateY: [100, 0],
easing: "easeOutExpo",
duration: 1400,
delay: function(el, i) {
return 30 * i;
}
}).add({
targets: '.ml16',
opacity: 0,
duration: 1000,
easing: "easeOutExpo",
delay: 1000
});
.wrap {
width: 700px;
margin: 100px auto;
}
.ml16 {
color: #402d2d;
padding: 40px 0;
font-weight: 800;
font-size: 2em;
text-transform: uppercase;
letter-spacing: 0.5em;
overflow: hidden;
text-transform: uppercase;
font-family: sans-serif;
}
.ml16 .letter {
display: inline-block;
line-height: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script>
<div class="wrap">
<h1 class="ml16"><span>Made with love for a testing purpose</span></h1>
</div>
Can someone help me pushing my incomplete idea to a destination?
What you need to do is wrap each word in another span (say, <span class="word"></span>) and set an overflow: hidden to that - see this fiddle: https://jsfiddle.net/w5uz4mex/
This will ensure that each word independently gets 'hidden' as it animates.
// Wrap every word in a span
$('.ml16').each(function() {
let text = $(this).text();
let words = text.split(' ');
// Clear current element
this.innerHTML = '';
// Loop through each word, wrap each letter in a span
for(let word of words) {
let word_split = word.replace(/([^\x00-\x80]|\w)/g, "<span class='letter'>$&</span>");
// Wrap another span around each word, add word to header
this.innerHTML += '<span class="word">' + word_split + '</span>';
}
});
anime.timeline({
loop: true
})
.add({
targets: '.ml16 .letter',
translateY: [100, 0],
easing: "easeOutExpo",
duration: 1400,
delay: function(el, i) {
return 30 * i;
}
}).add({
targets: '.ml16',
opacity: 0,
duration: 1000,
easing: "easeOutExpo",
delay: 1000
});
.wrap {
width: 700px;
margin: 100px auto;
}
.ml16 {
color: #402d2d;
padding: 40px 0;
font-weight: 800;
font-size: 2em;
text-transform: uppercase;
letter-spacing: 0.5em;
overflow: hidden;
text-transform: uppercase;
font-family: sans-serif;
}
.ml16 .word {
display: inline-block;
overflow: hidden;
height: 2em;
margin: 0 0.4em;
}
.ml16 .letter {
display: inline-block;
line-height: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrap">
<h1 class="ml16">Made with love for a testing purpose</h1>
</div>
Edit: As a bonus (unrelated) this can be done very simply without jQuery, and instead utilising CSS animations. This also gives you the benefit of very easily being able to add new animations via the CSS, without having to touch the JS. This is just a quick demo, so should be used as a starting point only (i.e. it has not been tested for any production environment).
See below for an example of slideUp, slideDown and zoomIn
/**
* Create new class for sliding text
*
* #params {Element} wrapper - HTML element with text content
*/
class TextSliderUpper {
constructor(wrapper) {
this.wrapper = wrapper;
// Set delay between characters (in ms)
this.delay = 40;
// Wrap content in relevant wrappers
this._wrapContent();
}
_wrapContent() {
let words = this.wrapper.textContent.split(' ');
let delay = 0;
let content = '';
// Loop through each word, wrap each character in a span
words.forEach((word, multiplier) => {
let word_split = word.split(/([^\x00-\x80]|\w)/g);
let word_content = '';
// Look through each letter, add a delay (incremented)
word_split.forEach((char, index) => {
delay += this.delay;
word_content += `<span style="animation-delay: ${delay}ms">${char}</span>`;
});
// Add spacing between words
if (content !== '') content += ' ';
// Add wrapped words to content
content += `<span>${word_content}</span>`;
})
// Add content to wrapper
this.wrapper.innerHTML = content;
}
init() {
this.wrapper.classList.add('show');
}
}
// Get a list of all headers
let headers = document.querySelectorAll('[data-animate]');
// Loop through, add relevant class
Array.from(headers).forEach(header => {
let slideHeader = new TextSliderUpper(header);
// Allow for delays? Sure!
let delay = header.dataset.delay || 0;
// Delay class (if necessary)
setTimeout(() => {
slideHeader.init();
}, delay)
})
body {
font-family: sans-serif;
}
h1 {
text-transform: uppercase;
font-weight: bold;
letter-spacing: 0.1em;
}
[data-animate] {
line-height: 1.2em;
}
[data-animate] > span {
display: inline-block;
height: 1.2em;
overflow: hidden;
}
[data-animate] > span > span {
display: none;
animation: 3s cubic-bezier(0, 1.2, 0.1, 0.9);
animation-fill-mode: backwards;
}
[data-animate].show > span > span {
display: inline-block;
}
[data-animate=slideup] > span > span {
animation-name: slideUp;
}
[data-animate=zoomin] > span > span {
animation-name: zoomIn;
}
[data-animate=slidedown] > span > span {
animation-name: slideDown;
}
#keyframes slideUp {
from {
opacity: 0;
transform: translate(0, 1.2em);
}
}
#keyframes zoomIn {
from {
opacity: 0;
transform: scale(0);
}
}
#keyframes slideDown {
from {
opacity: 0;
transform: translate(0, -1.2em);
}
}
<h1 data-animate="slideup">This is some text. Hello there!</h1>
<hr />
<h1 data-animate="zoomin" data-delay="2000">I am delayed!</h1>
<hr />
<h1 data-animate="slidedown" data-delay="7000">I am late to the party!</h1>
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.
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.