Delay on jquery keyup? - javascript

I'm trying to create my own "autocomplete", but when I type a letter (eg. w for word), then there's a splitsecond delay - enough to annoy the eye.
Here's my testcode:
CSS:
#txtSearchAutocomplete {
background-color: white !important;
position: absolute;
top: 0;
width: 100%;
font-size: 20px !important;
border: none !important;
color: gray;
}
#txtSearch {
background-color: transparent !important;
position: absolute;
top: 0;
width: 100%;
font-size: 20px !important;
border: none !important;
}
HTML:
<span style="position: relative; display: inline-block; width:100%; top: -18px;">
<input type="text" id="txtSearchAutocomplete" disabled >
<input type="text" id="txtSearch">
</span>
JS:
$(document).ready(function($) {
$("#txtSearch").focus();
$("#txtSearch").keyup(function(e) {
var autocomplete = ['word', 'excel'];
var $txtAutocomplete = $("#txtSearchAutocomplete");
var txt = $("#txtSearch").val().trim().toLowerCase();
$txtAutocomplete.val("");
if (txt == "") return;
for (i = 0; i < autocomplete.length; i++) {
var entry = autocomplete[i];
if (entry.indexOf(txt) == 0) {
$txtAutocomplete.val(entry);
break;
};
};
});
});
And a fiddle sample:
https://jsfiddle.net/25gwz1qu/1/
If you type in the letter w - delete it - type it again and so on, then you will notice a small delay. It might seam that the delay is a bit longer in IE.
Any idea how to get rid of this delay?
Thanks

The reason for the delay you are seeing is because the event triggers once the user lets go of the key. In that case, the oninput is the way to go. The event triggers when the textbox input changes.
$("#txtSearch").on('input', function(e) { ... })

Please take a look on my solution with comments that explain why I did those changes and here is a Working Fiddle.
On my machine the auto-complete is almost instant after those modifications.
$(document).ready(function($) {
// i had moved all selectors outside the function so the havy dom selection will happen only once
var autocomplete = ['word', 'excel'];
var $txtAutocomplete = $("#txtSearchAutocomplete");
var $searchElement = $("#txtSearch");
$searchElement.focus();
// In Jquery on works faster than on key up, cause user lets go of the key.
$searchElement.on('input',function(e) {
var txt = $searchElement.val().trim().toLowerCase();
// I had replaced the element to be a div and not a input cause the div element is much light weight and faster to draw for the browser
$txtAutocomplete.text("");
if (txt == "")
return;
for (i = 0; i < autocomplete.length; i++) {
var entry = autocomplete[i];
if (entry.indexOf(txt) == 0) {
$txtAutocomplete.text(entry);
break;
};
};
});
});

try this,
$(document).ready(function($) {
$("#txtSearch").focus();
$("#txtSearch").on('input',function(e) {
var autocomplete = ['word', 'excel'];
var $txtAutocomplete = $("#txtSearchAutocomplete");
var txt = $("#txtSearch").val().trim().toLowerCase();
$txtAutocomplete.val("");
if (txt == "") return;
for (i = 0; i < autocomplete.length; i++) {
var entry = autocomplete[i];
if (entry.indexOf(txt) == 0) {
$txtAutocomplete.val(entry);
break;
};
};
});
});

Related

Adding various pictures depending on given value in Forums

I have two png pictures, which I want to add on the top of pictures, depending on what certain setting is applied on the certain profile. I found a decent tutorial which could help me, it is here: http://fmdesign.forumotion.com/t279-profile-field-for-custom-post-profiles
Now, it only says how to add borders, background-and text color to a certain picture. I found an other post here on Stackoverflow, but I don't have the faintest idea how to change it to suit my needs. The thread is here: Insert image object into HTML
Now, my code that I want to use looks like this (my browser doesn't throw any errors for me, but it can nevertheless be rich with inappropriate coding and bugs, I am new to scripts, please be patient with me):
var delikeret = document.getElementById("dkepkeret");
var eszakikeret = document.getElementById("ekepkeret");
function extraProfileImage() {
var field = 'Hovatartozás';
customProfile
({
value: 'Észak',
keret: eszakikeret,
});
customProfile
({
value: 'Dél',
keret: delikeret,
});
customProfile({ value: '.*?', remove: True }); // remove field from profiles
function customProfile(o) {
var reg = new RegExp('<span class="label"><span style="color:#[a-f0-9]{6};">'+field+'</span>\\s:\\s</span>(\\s|)'+o.value+'<br>','i');
for (var i = 0, p = $('.postprofile, .user, .postdetails.poster-profile'); i < p.length; i++) {
if (p[i].innerHTML.match(reg)) {
if (o.remove) p[i].innerHTML = p[i].innerHTML.replace(reg, '');
} else {
p[i].style.backgroundImage: "url('" + o.keret + "')";
//p[i].appendChild(o.keret);
//p[i].style.background = o.keret;
//p[i].style.backgroundPosition = "center center";
//p[i].id = getElementById("o.keret");
}
}
}
}
var info = 'Plugin developed by Ange Tuteur for customizing post profiles. For help with this plugin, please see the following link : http://fmdesign.forumotion.com/t279-profile-field-for-custom-post-profiles';
$(document).ready(function() {
extraProfileImage();
});
I would be glad to receive any help!
PS: I have done everything else according to the Forumotion tutorial, although the images with "dkepkeret" and "ekepkeret" are stored elsewhere on the page, could that be a problem?
Thanks in advance!
So my code works somehow, here it is how it looks now:
var delikeret = new Image();
var eszakikeret = new Image();
delikeret.src = 'http://p.coldline.hu/2018/01/22/2748437-20180122-B8YiFj.png';
eszakikeret.src = 'http://p.coldline.hu/2018/01/22/2748438-20180122-9sWitv.png';
function extraProfileImage() {
var field = 'Hovatartozás';
customProfile
({
value: 'Észak',
});
customProfile
({
value: 'Dél',
});
// customProfile({ value: '.*?', remove: True }); // remove field from profiles
function customProfile(o) {
var reg = new RegExp(''+field+'\s:\s(\s|)'+o.value+'','i');
for (var i = 0, p = $('.postprofile, .user, .postdetails.poster-profile'); i < p.length; i++) {
if (p[i].innerHTML.match(reg)) {
if (o.remove) p[i].innerHTML = p[i].innerHTML.replace(reg, '');
} else {
if (o.value == "Észak") {
p[i].append(eszakikeret);
} else if (o.value == "Dél") {
p[i].append(delikeret);
}
}
}
}
}
But if I change the value of a given profile, it doesn't append the image. How to fix that?
So I could finally make my code free of bugs, here it is how it looks.
function extraProfileImage() {
var field = 'Hovatartozás';
customProfile
({
value: 'Észak',
});
customProfile
({
value: 'Dél',
});
//customProfile({ value: '.*?', remove: true }); // remove field from profiles
function customProfile(o) {
var reg = new RegExp('<span class="label"><span style="color:#[a-f0-9]{6};">'+field+'</span>\\s:\\s</span>(\\s|)'+o.value+'<br>','i');
for (var i = 0, p = $('.postprofile, .user, .postdetails.poster-profile'); i < p.length; i++) {
if (p[i].innerHTML.match(reg)) {
if (o.remove) p[i].innerHTML = p[i].innerHTML.replace(reg, '');
} else {
if (o.value == "Észak") {
p[i].classList.add("eszakikeret");
} else if (o.value == "Dél") {
p[i].classList.add("delikeret");
}
}
}
}
}
var info = 'Plugin developed by Ange Tuteur for customizing post profiles. For help with this plugin, please see the following link : http://fmdesign.forumotion.com/t279-profile-field-for-custom-post-profiles';
$(document).ready(function() {
extraProfileImage();
});
I had to set the .postprofile width to 210px so the border image doesn't stretch out for nothing.
The code added to my CSS is the following:
/* custom profile default*/ .postprofile, .user, .postdetails.poster-profile {
position: relative;
border:1px solid transparent;
padding:3px;
margin:3px;
z-index: 1;
}
/* ipb fix */ #ipbwrapper .postprofile {margin:0;
position: absolute;
top: 0;
left: 0;
}
.delikeret{
border-image-source: url(http://p.coldline.hu/2018/01/22/2748438-20180122-9sWitv.png);
border-image-slice: 20%;
border-image-outset: 10px;
border-image-width: 60px;
border-image-repeat: round;
}
.eszakikeret{
border-image-source: url(http://p.coldline.hu/2018/01/22/2748437-20180122-B8YiFj.png);
border-image-slice: 20%;
border-image-outset: 10px;
border-image-width: 60px;
border-image-repeat: round;
}
So I made it. If anyone would show future interest.

Scroll to position WITHIN a div (not window) using pure JS

PURE JS ONLY PLEASE - NO JQUERY
I have a div with overflow scroll, the window (html/body) never overflows itself.
I have a list of anchor links and want to scroll to a position when they're clicked.
Basically just looking for anchor scrolling from within a div, not window.
window.scrollTo etc. don't work as the window never actually overflows.
Simple test case http://codepen.io/mildrenben/pen/RPyzqm
JADE
nav
a(data-goto="#1") 1
a(data-goto="#2") 2
a(data-goto="#3") 3
a(data-goto="#4") 4
a(data-goto="#5") 5
a(data-goto="#6") 6
main
p(data-id="1") 1
p(data-id="2") 2
p(data-id="3") 3
p(data-id="4") 4
p(data-id="5") 5
p(data-id="6") 6
SCSS
html, body {
width: 100%;
height: 100%;
max-height: 100%;
}
main {
height: 100%;
max-height: 100%;
overflow: scroll;
width: 500px;
}
nav {
background: red;
color: white;
position: fixed;
width: 50%;
left: 50%;
}
a {
color: white;
cursor: pointer;
display: block;
padding: 10px 20px;
&:hover {
background: lighten(red, 20%);
}
}
p {
width: 400px;
height: 400px;
border: solid 2px green;
padding: 30px;
}
JS
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p'),
main = document.querySelector('main');
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function(){
var linkID = this.getAttribute('data-goto').slice(1);
for (var j = 0; j < links.length; j++) {
if(linkID === paras[j].getAttribute('data-id')) {
window.scrollTo(0, paras[j].offsetTop);
}
}
})
}
PURE JS ONLY PLEASE - NO JQUERY
What you want is to set the scrollTop property on the <main> element.
var nav = document.querySelector('nav'),
main = document.querySelector('main');
nav.addEventListener('click', function(event){
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
main.scrollTop = scrollTarget.offsetTop;
}
});
You'll notice a couple of other things I did different:
I used event delegation so I only had to attach one event to the nav element which will more efficiently handle clicks on any of the links.
Likewise, instead of looping through all the p elements, I selected the one I wanted using an attribute selector
This is not only more efficient and scalable, it also produces shorter, easier to maintain code.
This code will just jump to the element, for an animated scroll, you would need to write a function that incrementally updates scrollTop after small delays using setTimeout.
var nav = document.querySelector('nav'),
main = document.querySelector('main'),
scrollElementTo = (function () {
var timerId;
return function (scrollWithin, scrollTo, pixelsPerSecond) {
scrollWithin.scrollTop = scrollWithin.scrollTop || 0;
var pixelsPerTick = pixelsPerSecond / 100,
destY = scrollTo.offsetTop,
direction = scrollWithin.scrollTop < destY ? 1 : -1,
doTick = function () {
var distLeft = Math.abs(scrollWithin.scrollTop - destY),
moveBy = Math.min(pixelsPerTick, distLeft);
scrollWithin.scrollTop += moveBy * direction;
if (distLeft > 0) {
timerId = setTimeout(doTick, 10);
}
};
clearTimeout(timerId);
doTick();
};
}());
nav.addEventListener('click', function(event) {
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
scrollElementTo(main, scrollTarget, 500);
}
});
Another problem you might have with the event delegation is that if the a elements contain child elements and a child element is clicked on, it will be the target of the event instead of the a tag itself. You can work around that with something like the getParentAnchor function I wrote here.
I hope I understand the problem correctly now: You have markup that you can't change (as it's generated by some means you have no control over) and want to use JS to add functionality to the generated menu items.
My suggestion would be to add id and href attributes to the targets and menu items respectively, like so:
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p');
for (var i = 0; i < links.length; i++) {
links[i].href=links[i].getAttribute('data-goto');
}
for (var i = 0; i < paras.length; i++) {
paras[i].id=paras[i].getAttribute('data-id');
}

Get only the ellipsis text using jquery

Nice code, just wondered if it is possible to query and get the ellipsis text (i.e. with the dots in and not the original text)?
If I add the text
This is a long sentence
and (using the relevant css for ellipsis) it gets shortened to
This is a long sen ...
Is there a way to get the text
"This is a long sen ..."
from the $("p") DOM object rather than the original text?
Try that:
function getEllipsis(command, characters) {
for (var i = command.length; i >= 0; i--) {
if (command.substring(0, i).length < characters) {
if (i < command.length) {
command = command.substring(0, i) + "...";
}
return command;
}
}
}
console.log(getEllipsis("I am a long sentence",16))
console.log(getEllipsis("But I am even longer",20))
I have a rough draft that needs some browser-specific tweaking.
JavaScript:
jQuery.fn.getShowingText = function () {
// Add temporary element for measuring character widths
$('body').append('<div id="Test" style="padding:0;border:0;height:auto;width:auto;position:absolute;display:none;"></div>');
var longString = $(this).text();
var eleWidth = $(this).innerWidth();
var totalWidth = 0;
var totalString = '';
var finished = false;
var ellipWidth = $('#Test').html('…').innerWidth();
var offset = 7; // seems to differ based on browser (6 for Chrome and 7 for Firefox?)
for (var i = 0;
(i < longString.length) && ((totalWidth) < (eleWidth-offset)); i++) {
$('#Test').text(longString.charAt(i));
totalWidth += $('#Test').innerWidth();
totalString += longString.charAt(i);
if(i+1 === longString.length)
{
finished = true;
}
}
$('body').remove('#Test'); // Clean up temporary element
if(finished === false)
{
return totalString.substring(0,totalString.length-3)+"…";
}
else
{
return longString;
}
}
console.log($('#ellDiv').getShowingText());
CSS:
#Test {
padding:0;
border:0;
height: auto;
width: auto;
position:absolute;
white-space: pre;
}
div {
width: 100px;
white-space: nowrap;
border: 1px solid #000;
overflow:hidden;
text-overflow:ellipsis;
padding:0;
}
With the caveat that the offset needs to change depending on the browser, unless someone can figure out what is causing it.
I suspect letter-spacing or similar?

Mimicng caret in a textarea

I am trying to mimic the caret of a textarea for the purpose of creating a very light-weight rich-textarea. I don't want to use something like codemirror or any other massive library because I will not use any of their features.
I have a <pre> positioned behind a textarea with a transparent background so i can simulate a highlighting effect in the text. However, I also want to be able to change the font color (so its not always the same). So I tried color: transparent on the textarea which allows me to style the text in any way I want because it only appears on the <pre> element behind the textarea, but the caret disappears.
I have gotten it to work fairly well, although it is not perfect. The main problem is that when you hold down a key and spam that character, the caret seems to always lag one character behind. Not only that, it seems to be quite resource heavy..
If you see any other things in the code that need improvement, feel free to comment on that too!
Here's a fiddle with the code: http://jsfiddle.net/2t5pu/25/
And for you who don't want to visit jsfiddle for whatever reason, here's the entire code:
CSS:
textarea, #fake_area {
position: absolute;
margin: 0;
padding: 0;
height: 400px;
width: 600px;
font-size: 16px;
font: 16px "Courier New", Courier, monospace;
white-space: pre;
top: 0;
left: 0;
resize: none;
outline: 0;
border: 1px solid orange;
overflow: hidden;
word-break: break-word;
padding: 5px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
#fake_area {
/* hide */
opacity: 0;
}
#caret {
width: 1px;
height: 18px;
position: absolute;
background: #f00;
z-index: 100;
}
HTML:
<div id="fake_area"><span></span></div>
<div id="caret"></div>
<textarea id="textarea">test</textarea>
JAVASCRIPT:
var fake_area = document.getElementById("fake_area").firstChild;
var fake_caret = document.getElementById("caret");
var real_area = document.getElementById("textarea");
$("#textarea").on("input keydown keyup propertychange click", function () {
// Fill the clone with textarea content from start to the position of the caret.
// The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line.
doStuff();
});
var timeout;
function doStuff() {
if(timeout) clearTimeout(timeout);
timeout=setTimeout(function() {
fake_area.innerHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001');
setCaretXY(fake_area, real_area, fake_caret, getPos("textarea"));
}, 10);
}
function getCaretPosition(el) {
if (el.selectionStart) return el.selectionStart;
else if (document.selection) {
//el.focus();
var r = document.selection.createRange();
if (r == null) return 0;
var re = el.createTextRange(), rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
return rc.text.length;
}
return 0;
}
function setCaretXY(elem, real_element, caret, offset) {
var rects = elem.getClientRects();
var lastRect = rects[rects.length - 1];
var x = lastRect.left + lastRect.width - offset[0] + document.body.scrollLeft,
y = lastRect.top - real_element.scrollTop - offset[1] + document.body.scrollTop;
caret.style.cssText = "top: " + y + "px; left: " + x + "px";
//console.log(x, y, offset);
}
function getPos(e) {
e = document.getElementById(e);
var x = 0;
var y = 0;
while (e.offsetParent !== null){
x += e.offsetLeft;
y += e.offsetTop;
e = e.offsetParent;
}
return [x, y];
}
Thanks in advance!
Doesn't an editable Div element solve the entire problem?
Code that does the highlighting:
http://jsfiddle.net/masbicudo/XYGgz/3/
var prevText = "";
var isHighlighting = false;
$("#textarea").bind("paste drop keypress input textInput DOMNodeInserted", function (e){
if (!isHighlighting)
{
var currentText = $(this).text();
if (currentText != prevText)
{
doSave();
isHighlighting = true;
$(this).html(currentText
.replace(/\bcolored\b/g, "<font color=\"red\">colored</font>")
.replace(/\bhighlighting\b/g, "<span style=\"background-color: yellow\">highlighting</span>"));
isHighlighting = false;
prevText = currentText;
doRestore();
}
}
});
Unfortunately, this made some editing functions to be lost, like Ctrl + Z... and when pasting text, the caret stays at the beginning of the pasted text.
I have combined code from other answers to produce this code, so please, give them credit.
How do I make an editable DIV look like a text field?
Get a range's start and end offset's relative to its parent container
EDIT: I have discovered something interesting... the native caret appears if you use a contentEditable element, and inside of it you use another element with the invisible font:
<div id="textarea" contenteditable style="color: red"><div style="color: transparent; background-color: transparent;">This is some hidden text.</div></div>
http://jsfiddle.net/masbicudo/qsRdg/4/
The lag is I think due to the keyup triggering the doStuff a bit too late, but the keydown is a bit too soon.
Try this instead of the jQuery event hookup (normally I'd prefer events to polling, but in this case it might give a better feel)...
setInterval(function () { doStuff(); }, 10); // 100 checks per second
function doStuff() {
var newHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001');
if (fake_area.innerHTML != newHTML) {
fake_area.innerHTML = newHTML;
setCaretXY(fake_area, real_area, fake_caret, getPos("textarea"));
}
}
...or here for the fiddle:
http://jsfiddle.net/2t5pu/27/
this seems to work great and doesn't use any polls, just like i was talking about in the comments.
var timer=0;
$("#textarea").on("input keydown keyup propertychange click paste cut copy mousedown mouseup change", function () {
clearTimeout(timer);
timer=setTimeout(update, 10);
});
http://jsfiddle.net/2t5pu/29/
maybe i'm missing something, but i think this is pretty solid, and it behaves better than using intervals to create your own events.
EDIT: added a timer to prevent que stacking.

Javascript for-loop to control button click states

I'm new to JavaScript but moving over from ActionScript, so I'm using a lot of AS3 logic and not sure what's possible and not.
I have a series of 5 dots for an image slider nav. The dots are just CSS styled dots, so I'm trying to make it so I can control the colors using element.style.backgroundColor.
Here's my script:
function btnFeatured(thisBtn) {
btnFeatured_reset();
for (i = 1; i <= 5; i++) {
if (thisBtn === document.getElementById("dotFeat" + i)) {
document.getElementById("dotFeat" + i).style.backgroundColor = "#ffae00";
}
}
}
function btnFeatured_reset() {
for (i = 1; i <= 5; i++) {
document.getElementById("dotFeat" + i).style.backgroundColor = "#969696";
}
}
Seems to work just fine, but when I click the dot, it turns orange (ffae00) and then immediately turns back to gray (969696).
And just in case, here's the style I'm using for the dots:
#featured-nav a {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #969696;
border-bottom: none;
margin: 0 14px;
}
#featured-nav a:hover {
background-color: #ffae00;
border-bottom: none;
}
And my html:
Change the HTML to
test
test
test
test
test
and the JS:
function btnFeatured(thisBtn) {
for (i = 1; i <= 5; i++) {
var state = parseInt(thisBtn.id.slice(-1),10) == i,
elem = document.getElementById("dotFeat" + i);
elem.style.backgroundColor = (state ? "#ffae00" : "#969696");
}
return false;
}
FIDDLE
Even better would be to not use inline JS, but proper event handlers.

Categories

Resources