Repeat same JS function multiple times on same page - javascript

I have a large form and a JS to display a hidden DIV with a warning graphic if the user starts inputting with CAPS LOCK enabled. I have the DIV positioned to appear within the form text field. What I need, though, is to repeat this function in several separate fields, each field calling on a different class so that the warning graphic only appears in the specific field in which the user is currently typing. I'll post the JS followed by the CSS.
<script type="text/javascript">
var existing = window.onload;
window.onload = function()
{
if(typeof(existing) == "function")
{
existing();
}
loadCapsChecker();
}
function loadCapsChecker()
{
capsClass = "capLocksCheck";
capsNotice = "capsLockNotice";
var inputs = document.getElementsByTagName('INPUT');
var elements = new Array();
for(var i=0; i<inputs.length; i++)
{
if(inputs[i].className.indexOf(capsClass) != -1)
{
elements[elements.length] = inputs[i];
}
}
for(var i=0; i<elements.length; i++)
{
if(document.addEventListener)
{
elements[i].addEventListener("keypress",checkCaps,"false");
}
else
{
elements[i].attachEvent("onkeypress",checkCaps);
}
}
}
function checkCaps(e)
{
var pushed = (e.charCode) ? e.charCode : e.keyCode;
var shifted = false;
if(e.shiftKey)
{
shifted = e.shiftKey;
}
else if (e.modifiers)
{
shifted = !!(e.modifiers & 4);
}
var upper = (pushed >= 65 && pushed <= 90);
var lower = (pushed >= 97 && pushed <= 122);
if((upper && !shifted) || (lower && shifted))
{
if(document.getElementById(capsNotice))
{
document.getElementById(capsNotice).style.display = 'block';
}
else
{
alert("Please disable Caps Lock.");
}
}
else if((lower && !shifted) || (upper && shifted))
{
if(document.getElementById(capsNotice))
{
document.getElementById(capsNotice).style.display = 'none';
}
}
}
</script>
And the CSS:
#capsLockNotice {
position: relative;
display: none;
}
#capsLockNotice img {
position: absolute;
right: 5px;
top: -28px;
}
Then I put a "capLocksCheck" class on the input field, followed by this HTML:
<div id="capsLockNotice">
<img src="/images/capslock-notice.png" title="Please disable Caps Lock." alt="Please disable Caps Lock." />
</div>
What I need to do is have each of several specific form fields to call on its own unique Div Class so the warning graphic only appears in the specific field in which the user is currently typing. How can I modify the JS to allow different fields to call on different classes? I tried copying and pasting the entire code a second time, and changed
capsClass = "capLocksCheck";
capsNotice = "capsLockNotice";
to
capsClass = "capLocksCheck2";
capsNotice = "capsLockNotice2";
but obviously that didn't work. It just disabled the function entirely.
Thanks in advance for any help.

Your window.onload function is an anonymous function which calls loadCapsChecker();
window.onload = function()
{ ....
loadCapsChecker();
... //rest of your code
...
However loadCapsChecker(); was not defined as a global variable (Yes in JS variables can be functions ). Hence your window.onload function has no idea what loadCapsChecker(); refers to.
Try to declare the function loadCapsChecker(); with a global scope. It should work fine.
declare it before usage like this
var loadCapsChecker = function (){
// Your function code same as above for loadCapschecker in your code
};
window.onload = function()
{ ....
loadCapsChecker(); // Now this will because it can see the var "loadCapsChecker"
... //rest of your code
...
Hope that helps :)

besides #woofmeow answer
you can get the one in focus using document.activeElement
see How do I find out which DOM element has the focus?
as a side note
why don't you just change the active form field text? or better off just use the alert
or some pop-up div

Related

JavaScript waitFunction

I’m facing a small issue in JavaScript. I need to to make a code stop and do nothing for a while. I tried setTimeout, but it only scheludes the function, continues in executing the code and then comes back. I really need to wait for the user to put some value in the input field and then press the button. The sleep function on the beginning of my code works, but the code somehow stops showing my html input form and button. I can’t figure out why. I also can’t use onclick attribute on the submit button, because of the same problem. Does someone know what can be the problem here??
var returning = 0; // variable for deciding which part of function to use
function sleep(milliseconds) { // sleep method found here on stackoverflow
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
function acceptValue(){
// show an input field with button next to it
if (returning == 0) {
var co = document.createElement("input"); // show input field and set attributes
var kam = document.getElementById("telo");
var indexId = 0;
while(document.getElementById("pole" + indexId) != null) {
indexId++; // look for closest unused id name
}
co.setAttribute("id", "pole" + indexId);
kam.appendChild(co);
var co1 = document.createElement("input");
var kam1 = document.getElementById("telo");
var indexId1 = 0;
while(document.getElementById("cudlik" + indexId1) != null) {
indexId1++; // look for closest unused id name
}
co1.setAttribute("id", "cudlik" + indexId1); // show button and set attributes
co1.setAttribute("type", "submit");
co1.setAttribute("value", ">");
co1.setAttribute("onclick", "vraceni()");
kam1.appendChild(co1);
console.log(document);
document.getElementById("telo").appendChild(document.createElement("br"));
returning = 1;
acceptValue();
} else if (vrat == 1) {
sleep(500);
acceptValue();
}
} else {
var indexPole = 0;
while (document.getElementById("pole" + indexPole) != null) {
indexPole++;
}
vrat = 0;
return document.getElementById("pole" + (indexPole - 1)).value; // return the value from last shown input field
}
}
function returnFunc() {
vrat = 2; // called from html button
}
Thanks,
Adam Hendrych
I think the feature you are attempting to create here may need some re-architecting. It feels very strange to have all these while loops and sleep tricks in place.
What you describe wanting this code to do is basically the default behavior of how inputs and buttons work. A simple form containing an input and submit button, with an onsubmit handler on the form, should meet the "accept input and fire an action when the button is pressed" requirement.
An example

How to build a Smart Compose like Gmail? Possible in a textarea?

The new predictive type feature Smart Compose of Gmail is quite interesting.
Let's say we want to implement such a functionality ourselves:
User enters beginning of text, e.g. How and in gray behind it appears are you?.
User hits TAB and the word tomorrow is set.
Example:
Can a textarea with Javascript be used to achieve this?
And if not, how could this be implemented otherwise?
My previous answer got deleted, so here's a better attempt at explaining how I've somewhat replicated Smart Compose. My answer only focuses on the pertinent aspects. See https://github.com/jkhaui/predictable for the code.
We are using vanilla js and contenteditable in our solution (just like Gmail does). I bootstrap my example with create-react-app and Medium-Editor, but neither React nor Medium-Editor are necessary.
We have a database of "suggestions" which can be an array of words or phrases. For our purposes, in my example, I use a static array containing 50,000+ common English phrases. But you can easily see how this could be substituted for a dynamic data-source - such as how Gmail uses its neural network API to offer suggestions based on the current context of users' emails: https://ai.googleblog.com/2018/05/smart-compose-using-neural-networks-to.html
Smart Compose uses JavaScript to insert a <span></span> element immediately after the word you are writing when it detects a phrase to suggest. The span element contains only the characters of the suggestion that have not been typed.
E.g. Say you've written "Hi, how a" and a suggestion appears. Let's say the entire suggestion is "how are you going today". In this case, the suggestion is rendered as "re you going today" within the span. If you continue typing the characters in the placeholder - such as "Hi, how are you goi" - then the text content of the span changes dynamically - such that "ng today" is now the text within the span.
My solution works slightly differently but achieves the same visual effect. The difference is I can't figure out how to insert an inline span adjacent to the user's current text and dynamically mutate the span's content in response to the user's input.
So, Instead, I've opted for an overlay element containing the suggestion. The trick is now to position the overlay container exactly over the last word being typed (where the suggestion will be rendered). This provides the same visual effect of an inline typeahead suggestion.
We achieve correct positioning of the overlay by calculating the top + left coordinates for the last word being typed. Then, using JavaScript, we couple the top + left CSS attributes of the overlay container so that they always match the coordinates of the last word. The tricky part is getting these coordinates in the first place. The general steps are:
Call window.getSelection().anchorNode.data.length which retrieves the current text node the user is writing in and returns its length, which is necessary to calculate the offset of the last word within its parent element (explained in the following steps).
For simplicity's sake, only continue if the caret is at the end of the text.
Get the parent node of the current text node we're in. Then get the length of the parent node's text content.
The parent node's text length - the current text node's (i.e the last word's) text length = the offset position of the last text node within its contenteditable parent.
Now we have the offset of the last word, we can use the various range methods to insert a span element immediately preceding the last word: https://developer.mozilla.org/en-US/docs/Web/API/Range
Let's call this span element a shadowNode. Mentally, you can now picture the DOM as follows: we have the user's text content, and we have a shadowNode placed at the position of the last word.
Finally, we call getBoundingClientRect on the shadowNode which returns specific metadata, including the top + left coordinates we're after.
Apply the top + left coordinates to the suggestions overlay container and add the appropriate event handlers/listeners to render the suggestion when Tab is pressed.
Visit this link for documentation https://linkkaro.com/autocomplete.html .
May be you need to make few adjustment in CSS ( padding and width ).
I hope it will help.[![
$(document).ready(function(){
//dummy random output. You can use api
var example = {
1:"dummy text 1",
2:"dummy text 2"
};
function randomobj(obj) {
var objkeys = Object.keys(obj)
return objkeys[Math.floor(Math.random() * objkeys.length)]
}
var autocomplete = document.querySelectorAll("#autocomplete");
var mainInput = document.querySelectorAll("#mainInput");
var foundName = '';
var predicted = '';
var apibusy= false;
var mlresponsebusy = false;
$('#mainInput').keyup(function(e) {
//check if null value send
if (mainInput[0].value == '') {
autocomplete[0].textContent = '';
return;
}
//check if space key press
if (e.keyCode == 32) {
CallMLDataSetAPI(e);
scrolltobototm();
return;
}
//check if Backspace key press
if (e.key == 'Backspace'){
autocomplete[0].textContent = '';
predicted = '';
apibusy = true;
return;
}
//check if ArrowRight or Tab key press
if(e.key != 'ArrowRight'){
if (autocomplete[0].textContent != '' && predicted){
var first_character = predicted.charAt(0);
if(e.key == first_character){
var s1 = predicted;
var s2 = s1.substr(1);
predicted = s2;
apibusy = true;
}else{
autocomplete[0].textContent = '';
apibusy= false;
}
}else{
autocomplete[0].textContent = '';
apibusy= false;
}
return;
}else{
if(predicted){
if (apibusy == true){
apibusy= false;
}
if (apibusy== false){
mainInput[0].value = foundName;
autocomplete[0].textContent = '';
}
}else{
return;
}
}
function CallMLDataSetAPI(event) {
//call api and get response
var response = {
"predicted": example[randomobj(example)]
};
if(response.predicted != ''){
predicted = response.predicted;
var new_text = event.target.value + response.predicted;
autocomplete[0].textContent = new_text;
foundName = new_text
}else{
predicted = '';
var new_text1 = event.target.value + predicted;
autocomplete[0].textContent = new_text1;
foundName = new_text1
}
};
});
$('#mainInput').keypress(function(e) {
var sc = 0;
$('#mainInput').each(function () {
this.setAttribute('style', 'height:' + (0) + 'px;overflow-y:hidden;');
this.setAttribute('style', 'height:' + (this.scrollHeight+3) + 'px;overflow-y:hidden;');
sc = this.scrollHeight;
});
$('#autocomplete').each(function () {
if (sc <=400){
this.setAttribute('style', 'height:' + (0) + 'px;overflow-y:hidden;');
this.setAttribute('style', 'height:' + (sc+2) + 'px;overflow-y:hidden;');
}
}).on('input', function () {
this.style.height = 0;
this.style.height = (sc+2) + 'px';
});
});
function scrolltobototm() {
var target = document.getElementById('autocomplete');
var target1 = document.getElementById('mainInput');
setInterval(function(){
target.scrollTop = target1.scrollHeight;
}, 1000);
};
$( "#mainInput" ).keydown(function(e) {
if (e.keyCode === 9) {
e.preventDefault();
presstabkey();
}
});
function presstabkey() {
if(predicted){
if (apibusy == true){
apibusy= false;
}
if (apibusy== false){
mainInput[0].value = foundName;
autocomplete[0].textContent = '';
}
}else{
return;
}
};
});
#autocomplete { opacity: 0.6; background: transparent; position: absolute; box-sizing: border-box; cursor: text; pointer-events: none; color: black; width: 421px;border:none;} .vc_textarea{ padding: 10px; min-height: 100px; resize: none; } #mainInput{ background: transparent; color: black; opacity: 1; width: 400px; } #autocomplete{ opacity: 0.6; background: transparent;padding: 11px 11px 11px 11px; }
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<textarea id="autocomplete" type="text" class="vc_textarea"></textarea>
<textarea id="mainInput" type="text" name="comments" placeholder="Write some text" class="vc_textarea"></textarea>
]1]1

reduce into object orientated javascript

I have writen a little javascript. It is one of my first and I am still learning. I would like to reduce the line count and make it more efficient. I believe that Object Orientated Programming will be my best choice.
The script assigns a class to a button on hover.
gets elements with that class and performs various if functions to determine if the src attribute of the image button should be changed. The script also changes another image src attribute at the same time.
I am wondering if I can somehow condense the logic of the if statements into just one or two, then using variables perform the src attribute changes. But I dont know how to go about this...?
//assign navButtons to var buttons (creates array)
var buttons = document.getElementsByClassName("navButton");
//set buttonHover function
function buttonOn(){
if ( arrow == topArrow.mouseout ) {
newArrow = document.getElementById("topArrow");
newArrow.setAttribute("src", topArrow.mouseover);
menuText.setAttribute("src", topArrow.text);
}
if ( arrow == rightArrow.mouseout ) {
newArrow = document.getElementById("rightArrow");
newArrow.setAttribute("src", rightArrow.mouseover);
menuText.setAttribute("src", rightArrow.text);
}
if ( arrow == bottomArrow.mouseout ) {
newArrow = document.getElementById("bottomArrow");
newArrow.setAttribute("src", bottomArrow.mouseover);
menuText.setAttribute("src", bottomArrow.text);
}
if ( arrow == leftArrow.mouseout ) {
newArrow = document.getElementById("leftArrow");
newArrow.setAttribute("src", leftArrow.mouseover);
menuText.setAttribute("src", leftArrow.text);
}
}
//set buttonHover function
function buttonOff(){
if ( arrow != topArrow.mouseout ) {
resetArrow = document.getElementById("topArrow");
resetArrow.setAttribute("src", topArrow.mouseout);
menuText.setAttribute("src", start.text);
}
if ( arrow != rightArrow.mouseout ) {
resetArrow = document.getElementById("rightArrow");
resetArrow.setAttribute("src", rightArrow.mouseout);
menuText.setAttribute("src", start.text);
}
if ( arrow != bottomArrow.mouseout ) {
resetArrow = document.getElementById("bottomArrow");
resetArrow.setAttribute("src", bottomArrow.mouseout);
menuText.setAttribute("src", start.text);
}
if ( arrow != leftArrow.mouseout ) {
resetArrow = document.getElementById("leftArrow");
resetArrow.setAttribute("src", leftArrow.mouseout);
menuText.setAttribute("src", start.text);
}
}
//for each instance of buttons, assign class "active" onmouseover
for(var i = 0; i < buttons.length; ++i){
buttons[i].onmouseover = function() {
this.className = "active";
arrow = document.getElementsByClassName("active");
//get attribute
arrow = arrow[0].getAttribute("src");
console.log(arrow);
buttonOn();
};
}
//for each instance of buttons, remove class "active" onmouseout
for(var i = 0; i < buttons.length; ++i){
buttons[i].onmouseout = function () {
arrow = document.getElementsByClassName("active");
//get attribute
arrow = arrow[0].getAttribute("src");
buttonOff();
this.className = "";
};
}
Any help would be ace!
The JS Fiddle
Don't handle everything in JS, you can simply set a background image on the available anchor tags without using an img tag and then change the background image on :hover (best would be to use sprites). The only part where JS should kick in would be to change the text image, but there also not by changing an img tag src attribute.
You should preload all the text images as content (with a sensible alt text), position them over one another and then show/hide them according to what button was hovered.
Maybe I'll adjust your fiddle if I get to it but you could probably already optimize it with these information yourself.
You could improve the buttonOn function a bit, by removing the if statements and replacing them with a call to a factory class of some kind. For example:
function ArrowHandlerFactory(){
var self = this;
self.Create = function(arrow) {
alert(arrow);
if ( arrow == topArrow.mouseout ) {
return new topArrowHandler();
}
}
return {
Create: self.Create
}
}
var topArrowHandler = function(){
var self = this;
self.ArrowPressed = function() {
newArrow = document.getElementById("topArrow");
newArrow.setAttribute("src", topArrow.mouseover);
menuText.setAttribute("src", topArrow.text);
}
return {
ArrowPressed: self.ArrowPressed
}
}
var factory = new ArrowHandlerFactory();
//set buttonHover function
function buttonOn(){
var handler = factory.Create(arrow);
handler.arrowPressed();
}
The above isn't the complete code, but it gives you the basics to get started.
I'm not stating that the above is the best JavaScript ever, but the core idea of using a factory class, and then a specific class for each arrow direction, is still sound.

onload function revision to check value of a form

I am working on a website and I came across an interesting situation. In this particular website we are using a form that has given fields filled out that can be modified, etc. Part of this form gives the user the option between choosing one language or up to 6 languages. Each of these particular rows of the form are hidden unless the user clicks an add language button. There is also a remove language button. The problem that I am having is that there is an onload function that someone wrote to display the table on the my account page, but it only goes through and omits the sections of the table that are set to display:none; Here is the code for the current onload function:
<script type="text/javascript">
/* call onload with table id(s) */
function TR_set_toggle()
{
/* toggleRow method */
var toggleRow = function()
{
this.style.display = ((this.style.display == '') ? 'none' : '');
return false;
}
for (var oTable, a = 0; a < arguments.length; ++a)
{
oTable = document.getElementById(arguments[a]);
var r = 0, row, rows = oTable.rows;
while (row = rows.item(r++))
row.toggle = toggleRow;
}
}
onload = function()
{
TR_set_toggle('my_table');
}
</script>
It looks a little sloppy to me but maybe that's because I am new to javascript. Anyways, I want to change the function so it loads the table but also goes through each of the items that display none and check to see if they have input or not to display them. I don't understand the syntax of this.style.display = ((this.style.display == '') ? 'none' : ''); 1. How can I add an if statement into this line of code? 2. How can I check to see if a field has input or is set to the default? Any help is appreciated. Thanks.
How can I add an if statement into this line of code?
((this.style.display == '') ? 'none' : '');
is similar to
if( this.style.display == '' ) {
this.style.display == 'none'
}
else {
this.style.display = '';
}
How can I check to see if a field has input or is set to the default?
I dont understand your question. What do you mean with "field"?

Editing all external links with javascript

How can I go through all external links in a div with javascript, adding (or appending) a class and alt-text?
I guess I need to fetch all objects inside the div element, then check if each object is a , and check if the href attributen starts with http(s):// (should then be an external link), then add content to the alt and class attribute (if they don't exist create them, if they do exists; append the wanted values).
But, how do I do this in code?
This one is tested:
<style type="text/css">
.AddedClass
{
background-color: #88FF99;
}
</style>
<script type="text/javascript">
window.onload = function ()
{
var re = /^(https?:\/\/[^\/]+).*$/;
var currentHref = window.location.href.replace(re, '$1');
var reLocal = new RegExp('^' + currentHref.replace(/\./, '\\.'));
var linksDiv = document.getElementById("Links");
if (linksDiv == null) return;
var links = linksDiv.getElementsByTagName("a");
for (var i = 0; i < links.length; i++)
{
var href = links[i].href;
if (href == '' || reLocal.test(href) || !/^http/.test(href))
continue;
if (links[i].className != undefined)
{
links[i].className += ' AddedClass';
}
else
{
links[i].className = 'AddedClass';
}
if (links[i].title != undefined && links[i].title != '')
{
links[i].title += ' (outside link)';
}
else
{
links[i].title = 'Outside link';
}
}
}
</script>
<div id="Links">
<a name="_Links"></a>
FOO
FILE
SomeWhere
SomeWhere 2
SomeWhere 3
ElseWhere 1
ElseWhere 2
ElseWhere 3
BAR
Show/Hide
</div>
If you are on an account on a shared server, like http://big-server.com/~UserName/, you might want to hard-code the URL to go beyond the top level. On the other hand, you might want to alter the RE if you want http://foo.my-server.com and http://bar.my-server.com marked as local.
[UPDATE] Improved robustness after good remarks...
I don't highlight FTP or other protocols, they probably deserve a distinct routine.
I think something like this could be a starting point:
var links = document.getElementsByTagName("a"); //use div object here instead of document
for (var i=0; i<links.length; i++)
{
if (links[i].href.substring(0, 5) == 'https')
{
links[i].setAttribute('title', 'abc');
links[i].setAttribute('class', 'abc');
links[i].setAttribute('className', 'abc');
}
}
you could also loop through all the A elements in the document, and check the parent to see if the div is the one you are looking for
This can be accomplished pretty easily with Jquery. You would add this to the onload:
$("div a[href^='http']").each(function() {
$(this).attr("alt",altText);
var oldClassAttributeValue = $(this).attr("class");
if(!oldClassAttributeValue) {
$(this).attr("class",newClassAttributeValue);
}
});
You could modify this to add text. Class can also be modified using the css function.
My (non-framework) approach would be something along the lines of:
window.onload = function(){
targetDiv = document.getElementById("divName");
linksArray = targetDiv.getElementsByTagName("a");
for(i=0;i=linksArray.length;i++){
thisLink = linksArray[i].href;
if(thisLink.substring(4,0) = "http"){
linksArray[i].className += "yourcontent"; //you said append so +=
linksArray[i].alt += "yourcontent";
}
}
}
This is not tested but I would start like this and debug it from here.

Categories