What I am looking for is slightly subjective, but I am sure there is a better way to do this.
I am looking for a better way to perform javascript while a user is typing content into either a textarea or input box on a website. For instance, sites such as Google Docs are capable of saving changes to documents almost instantly without noticeable performance degradation. Many sites however use a bit of jQuery that might look like the following:
$("#element").on("keyup", function() { /* Do something */ });
This works fine for simple things like autocomplete in search boxes, but performance becomes a nightmare once you have any sizable corpus for it to have to deal with (or if a user types fast, yikes).
In trying to find a better way to analyze/save/what-have-you text as the user is typing, I started to do something like this:
var changed = false;
$("#element").on("keyup", function() { changed = true });
setInterval(function() { if(changed) { /* Do something */ changed = false; } }, 1000);
It seems to alleviate laggy or delayed text input, but to me it seems like a less than elegant solution.
So back to my question, is there a better way to have javascript execute when a corpus has been changed? Is there a solution outside of using intervals?
Thanks.
There is a jQuery plugin that does pretty much what you did.
Your example will be transformed into
$("#element").on("keyup", $.debounce(1000, function() { /* Do something */ }));
The code will execute after a user is not pressing any keys for 1000ms.
I have found a very good solution for this. This code will check whether the content has been changed and based on that it will save it otherwise the save functionality will not be executed !
Check out this demo JSFIDDLE
Here is the code :
HTML :
Content:<br>
<br>
(type some text into the textarea and it will get saved automatically)
<textarea rows="5" cols="25" id="content"></textarea>
<br>
<span id="sp_msg_saved" style="background-color:yellow; display:none">Content is saved as draft !</span>
JS:
var old_content = "";
function save_content()
{
var current_content = $('#content').val();
//check if content has been updated or not
if(current_content != old_content)
{
alert('content is updated ! Save via ajax');
old_content = current_content;
$('#sp_msg_saved').show(100);
$('#sp_msg_saved').fadeOut(3000);
}
}
setInterval(save_content,3000);
You can increase or decrease the amount of time for the save function to call by altering the values in setInterval function. Put the code for saving the content via ajax, that will save the current user content into your DB, I haven't included that one...
You can make your own little delay by using the window.setTimeout-Function:
var IntervalId = null;
function saveEdits(){
//Doing your savings...
}
$('input').keyup(function(){
if (IntervalId){
window.clearTimeout(IntervalId);
IntervalId = null;
}
IntervalId = window.setTimeout(function(){
saveEdits();
}, 3000);
});
Related
I have an problem with my site when I want to change the css style from the JavaScript it works but only for few seconds.
function validateForm() {
var fname = document.getElementById('<%=UserFnameTextBox.ClientID%>');
if (fname.value == "") {
document.getElementById("WarnUserFnameTextBox").style.opacity = 1;
document.getElementById('<%=UserFnameTextBox.ClientID%>').style.borderColor = "red";
getElementById('<%=UserFnameTextBox.ClientID%>').focus;
}
}
I'm using also Asp.net, that's why I wrote the ID like this
I want that the JS will save the style for as long that the user enter the textbox.
Multiple things here: I suggest that your validateForm() function triggers in an onClick on your submit-button, right? Does your button look somewhat like this?
<input type="submit" value="submit" onClick="validateForm()">
If this is the case, the reason why your styles work only for few seconds is simply that the website reloads. The styles are in effect, but the form is also triggering and send to the site, which you added in your <form action>. After reloading, the website will fall back to its default style, as if the errors never occured... which is correct on that instance of the site.
If you want to have it permanent, you have to disable the submit-button as long as there are invalid fields. You can make use of the required attribute for form elements as well, since the form won't submit as long as there are invalid fields. These can be styled as well.
Have a look at these CSS rules for that:
/* style all elements with a required attribute */
:required {
background: red;
}
You can make use of jQuery as well and disable the form-submit with preventDefault. You can take care of every style and adjust accordingly, as long as there empty / non-valid characters in your input-fields. I suggest combining this with the onKeyUp-function. This way you check everytime the users releases a key and can react as soon as your input is valid.
As an example with jQuery:
var $fname = $('#<%=UserFnameTextBox.ClientID%>');
var $textBox = $('#WarnUserFnameTextBox');
$fname.on("input", function() {
var $this = $(this);
if($this.val() == "") {
$textBox.show();
$this.focus().css("border", "1px solid red");
}
});
(thanks for pointing out my errors and optimizing the code, #mplungjan!).
To "disable" the actual form-submission, refer to this answer:
https://stackoverflow.com/a/6462306/3372043
$("#yourFormID").submit(function(e){
return false;
});
This is untested, feel free to point out my mistake, since I can't check it right now. You can play around on how you want to approach your "errorhandling", maybe switch to onKeyDown() or change(), that kind of depends on your needs / usecase.
Since your question isn't tagged with jQuery, have a look at this answer given by mplungjan as well, since it uses native JS without any framework.
https://stackoverflow.com/a/53777747/3372043
This is likely what you want. It will stop the form from being submitted and is reusing the field and resetting if no error
It assumes <form id="myForm"
window.addEventListener("load", function() {
document.getElementById("myForm").addEventListener("submit", function(e) {
var field = document.getElementById('<%=UserFnameTextBox.ClientID%>');
var error = field.value.trim() === "";
document.getElementById("WarnUserFnameTextBox").style.opacity = error ? "1" : "0"; // or style.display=error?"block":"none";
field.style.borderColor = error ? "red" : "black"; // reset if no error
if (error) {
field.focus();
e.preventDefault();
}
});
});
Weird problem. I'm modifying shop template:
https://demo.themeisle.com/shop-isle/product-category/clothing/dresses/
At this moment when you hover product's picture there will show "add to cart" button. This is .
Under picture there is price
I prepared code:
var from = document.getElementsByClassName("woocommerce-Price-amount amount");
jQuery(document).ready(function(){
jQuery.each(from, function(i, el) {
jQuery(el.parentNode.parentNode).find(jQuery(".product-button-wrap")).append(el);
});
});
Nothing happens. This code work only if I set timeout:
setTimeout(function() {
var from = document.getElementsByClassName("woocommerce-Price-amount amount");
jQuery(document).ready(function(){
jQuery.each(from, function(i, el) {
jQuery(el.parentNode.parentNode).find(jQuery(".product-button-wrap")).append(el);
});
});
}, 10000);
Of course timeout it's not a solution. I was trying to find out minimal time to obtain best behavior but it's impossible. I have feeling that every browser (and version...) needs personalized time setting.
I thought that after 24-hour break I will get some brillant idea, but that doesn't work, no more ideas.
--- EDIT ---
OK, thanks for pointed mixed common js with jquery - I will correct that later.
jQuery(document).ready(function(){
var from = document.getElementsByClassName("woocommerce-Price-amount amount");
jQuery.each(from, function(i, el) {
jQuery(el.parentNode.parentNode).find(jQuery(".product-button-wrap")).append(el);
console.log(el);
});
});
That's logical that var from should be inside ready but this still doesn't work. No effect.
If I use in loop console.log it will return for me html code of el.
--- EDIT ---
Thanks. While testing I noticed something. I wanted append element .woocommerce-Price-amount.amount to element .product-button-wrap. But how can I do that if element .product-button-wrap isn't originally in source? This object is created dynamically (I don't know how).
-- EDIT --
OK. I checked JS files and found code adding to DOM .product-button-wrap so I putted my code there and now everything works. Thanks for help.
The problem is because you're running your code before the DOM has loaded. You need to retrieve the elements within the document.ready event handler.
Also note that you have an odd mix of native JS and jQuery methods. I'd suggest using one or the other, like this:
jQuery(function($) {
$('.woocommerce-Price-amount.amount').each(function() {
$(this).parent().parent().find('.product-button-wrap').append(this);
});
});
Also note that .parent().parent() should be replace by a single call to closest(), but I can't give you an exact example of that without seeing your HTML.
https://jsfiddle.net/1vm0259x/ I want to have it so when the contents of #b changes it immediately makes the div display. Is that possible? I'm having to get a little hacky because of the limitations of a CMS plugin. I don't know jQuery very well.
Markup
<div id="a">
<span id="b">0 items</span>
</div>
jQuery
$(document).ready(function(){
if($('#b:contains("0 items")')) {
$('#a').css("display", "none");
}
if($('#b:not(:contains("0 items"))')) {
$('#a').css("display", "block");
}
});
The best way to monitor for text changes like this are to find a way to hook into some related and existing event in the browser and then see if the text has changed to what you want.
In the newer browsers (such as IE11+, recent versions of Chrome, Firefox and Safari), you can use a DOM MutationObserver to directly watch to see if the text nodes change.
The mutation callback is called anytime the children of the specified element are changed (children include the text nodes).
Here's some runnable code that watches for a text change in a div in this code snippet:
document.getElementById("go").addEventListener("click", function(e) {
var t = document.getElementById("test");
t.innerHTML = parseInt(t.innerHTML, 10) + 1;
});
var m = new MutationObserver(function(mRecords, obj) {
log("Current text value: " + document.getElementById("test").innerHTML);
}).observe(document.getElementById("test"), {childList: true, characterData: true, subtree: true});
function log(x) {
var d = document.createElement("div");
d.innerHTML = x;
document.body.appendChild(d);
}
<button id="go">Click Me to Change the Text</button><br><br>
<div id="test">1</div><br><br>
If you need support for older version of IE, then the next best thing would be to figure out what existing events in the browser precede the text change and monitor those events. When one of those events occurs, you can then check the text. For example, if the action that triggers the text change always comes after an Ajax call, you can monitor/hook Ajax calls on a system wide basis and then check your text after each Ajax call completes. Since this only ever does anything when other things are already happening in the web page, it's very efficient. Or, if the text only changes after a particular button is clicked or some text field is changed, you can monitor those DOM elements with event listeners.
To suggest how to do that more specifically, we'd need to see the details of your actual circumstance and would have to understand what events in the page lead to the changing text. Such short duration timers can also negatively affect the performance of things like animations running in your page or in other tabs.
It is NOT recommended to use a short duration timer to poll the DOM because this kills mobile battery life and, in fact, mobile browsers will attempt to delay or slow down any long running interval timers you use in order to try to preserve battery life.
On top of MutationObserver, eloquently put by #jfriend00, there is an older API available at our disposal as well by the name DOMSubtreeModified. Combine that with onpropertychange that of Internet Explorer and I believe you get a nice backward compatible change event. Take a look at the snippet below, not thoroughly tested though:
Snippet:
var myDIV=document.getElementById('a');
var mySpan=document.getElementById('b');
var myButton=document.getElementById('button');
var myResult=document.getElementById('result');
if(window.addEventListener){
myButton.addEventListener('click',onButtonClicked,false);
mySpan.addEventListener('DOMSubtreeModified',onSpanModified,false);
}else{
myButton.attachEvent('onclick',onButtonClicked);
mySpan.attachEvent('onpropertychange',onSpanModified);
}
function onButtonClicked(){
//mySpan.innerText=Math.random();
mySpan.innerHTML=Math.random();
}
function onSpanModified(){
myResult.innerHTML=mySpan.innerHTML;
}
<div id="a">
<span id="b">0</span>
</div>
<input id="button" type="button" value="Click Me" />
<span id="result"></span>
Hope this helps in some way though. Apologies if this was not what you were looking for and if I misunderstood your problem completely.
You can't really "watch" something in JavaScript since it's an event-driven language (there are a few exceptions to this rule when it comes to object properties, but it is not something that is commonly useful).
Instead you should set an interval that updates regularly. I used 50 millisecond intervals, you can choose to use whatever interval you like:
function update() {
if($('#b:contains("0 items")').length) {
$('#a').css("display", "none");
}
if($('#b:not(:contains("0 items"))').length) {
$('#a').css("display", "block");
}
}
$(document).ready(function () {
setInterval(update, 50);
});
I need help: I'm trying to make a javascript (with not much luck so far).
There are buttons on the page I generate with php, they should work as follows:
If I click the button for less than whatever seconds the link opens like if the button has: onClick="self.location='url'".
Else the button is held down for more than whatever seconds the link should open in a new tab like if the button had: onclick="window.open('url');"
It would be great if it could work for links also.
It's probably easy to do, but I have no js knowledge at all and I'm flooded with other stuff I actually know how to do so that is why I ask for Your help. I have already missed my deadline. :(
My goal is to make a php function to create buttons:
like : createbutton($name,$link,$class,$delay, ... );
But don't worry about that, I can do that.
Thanks for your help.
An Example here
var counter= 0;
doCount=function(){
setTimeout(function(){
counter++;
},1000);
}
doFunc=function(){
if(counter>2){
//do something if delay is greater than 2 second
}
else{
//do something if delay is less than 2 second
}
}
document.getElementById('myBtn').mousedown = doCount;
document.getElementById('myBtn').mouseup = doFunc;
Try this code
After a long struggle, I've finally found the only way to clear autofill styling in every browser:
$('input').each(function() {
var $this = $(this);
$this.after($this.clone()).remove();
});
However, I can’t just run this in the window load event; autofill applies sometime after that. Right now I’m using a 100ms delay as a workaround:
// Kill autofill styles
$(window).on({
load: function() {
setTimeout(function() {
$('.text').each(function() {
var $this = $(this);
$this.after($this.clone()).remove();
});
}, 100);
}
});
and that seems safe on even the slowest of systems, but it’s really not elegant. Is there some kind of reliable event or check I can make to see if the autofill is complete, or a cross-browser way to fully override its styles?
If you're using Chrome or Safari, you can use the input:-webkit-autofill CSS selector to get the autofilled fields.
Example detection code:
setInterval(function() {
var autofilled = document.querySelectorAll('input:-webkit-autofill');
// do something with the elements...
}, 500);
There's a bug open over at http://code.google.com/p/chromium/issues/detail?id=46543#c22 relating to this, it looks like it might (should) eventually be possible to just write over the default styling with an !important selector, which would be the most elegant solution. The code would be something like:
input {
background-color: #FFF !important;
}
For now though the bug is still open and it seems like your hackish solution is the only solution for Chrome, however a) the solution for Chrome doesn't need setTimeout and b) it seems like Firefox might respect the !important flag or some sort of CSS selector with high priority as described in Override browser form-filling and input highlighting with HTML/CSS. Does this help?
I propose you avoiding the autofill in first place, instead of trying to trick the browser
<form autocomplete="off">
More information: http://www.whatwg.org/specs/web-forms/current-work/#the-autocomplete
If you want to keep the autofill behaviour but change the styling, maybe you can do something like this (jQuery):
$(document).ready(function() {
$("input[type='text']").css('background-color', 'white');
});
$(window).load(function()
{
if ($('input:-webkit-autofill'))
{
$('input:-webkit-autofill').each(function()
{
$(this).replaceWith($(this).clone(true,true));
});
// RE-INITIALIZE VARIABLES HERE IF YOU SET JQUERY OBJECT'S TO VAR FOR FASTER PROCESSING
}
});
I noticed that the jQuery solution you posted does not copy attached events. The method I have posted works for jQuery 1.5+ and should be the preferred solution as it retains the attached events for each object. If you have a solution to loop through all initialized variables and re-initialize them then a full 100% working jQuery solution would be available, otherwise you have to re-initialize set variables as needed.
for example you do: var foo = $('#foo');
then you would have to call: foo=$('#foo');
because the original element was removed and a clone now exists in its place.