As you've probably found, there appears to be no equivalent way to add the following Excel form and associated VBA code to Google Sheets or Scripts or Forms:
Is there some add-in that can be used to pop up this image and its controls? This has to be used many times in an accounting sheet to categorize expenditures at tax time.
It may not look exactly the same but I was able to construct a custom dialog in a short period of time to show how HTML service can be used to produce similar results.
First I construct an HTML template that contains the 2 combo boxes with multiple lines.
HTML_Test.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('CSS_Test'); ?>
</head>
<body>
<div id="left">
<label for="expenseCategory">Expense Category</label><br>
<select id="expenseCategory" size="10">
</select>
</div>
<div id="middle">
<label for="expenseSubCategory">Expense Sub Category</label><br>
<select id="expenseSubCategory" size="10">
</select>
</div>
<?!= include('JS_Test'); ?>
</body>
</html>
Then a CSS file to contain all my element formatting.
CSS_Test.html
<style>
#expenseCategory {
width: 90%;
}
#expenseSubCategory {
width: 90%;
}
#left {
width: 25%;
float: left;
}
#middle {
width: 50%;
float: left;
}
</style>
And a javascript file for client side javascript. I've simply hard coded some data to show how the select elements are filled in but this could just as easily be done using template scripting, or google.script.run
<script>
var expenses = [["A","1","2","3"],
["B","4","5"],
["C","6","7","8","9","10"]
];
function expenseCategoryOnClick() {
try {
let expenseCategory = document.getElementById('expenseSubCategory');
expenseCategory.options.length = 0;
expenses[this.selectedIndex].forEach( (expense,index) => {
if( index > 0 ) {
let option = document.createElement("option");
let text = document.createTextNode(expense);
option.appendChild(text);
expenseCategory.appendChild(option);
}
}
);
}
catch(err) {
alert("Error in expenseCategoryOnClick: "+err)
}
}
(function () {
// load first expense
let expenseCategory = document.getElementById('expenseCategory');
expenseCategory.addEventListener("click",expenseCategoryOnClick);
expenses.forEach( expense => {
let option = document.createElement("option");
let text = document.createTextNode(expense[0]);
option.appendChild(text);
expenseCategory.appendChild(option);
}
);
expenseCategory = document.getElementById('expenseSubCategory');
expenses[0].forEach( (expense,index) => {
if( index > 0 ) {
let option = document.createElement("option");
let text = document.createTextNode(expense);
option.appendChild(text);
expenseCategory.appendChild(option);
}
}
);
}
)();
</script>
Then there is the server side code bound to a spreadsheet.
Code.gs
function onOpen(e) {
var menu = SpreadsheetApp.getUi().createMenu("Test");
menu.addItem("Show Test","showTest");
menu.addToUi();
}
// include(filename) required to include html files in the template
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function showTest() {
var html = HtmlService.createTemplateFromFile("HTML_Test");
html = html.evaluate();
html.setWidth(800);
SpreadsheetApp.getUi().showModalDialog(html,"Test");
}
The dialog looks like this. Many more html elements can be added as needed. This just shows the basics. This may be more difficult than an wysiwig html editor but I find I have better control of the appearance and function of my pages this way. Notice I clicked "C" and the sub category is filled in automatically.
How to find and highlight text in WKWebView? I have search option in my app which will search text inside WKWebview. I have to highlight the searched text in WKWebView.
I want to get "browser Find feature" in WKWebView.
I have tried some code in Mac browser which is working in Mac browser but not working in iPhone WKWebView.
I want search text between tags.
<!DOCTYPE html>
<html>
<head>
<title>Test page for Highlight Search Terms</title>
</head>
<body>
<input type="text" id="search" autofocus onkeyup="highlight(document.getElementById('search').value);" />
<div id="content">
<p>Here is some searchable <b>text</b> with some lápices in it, and more lápices, and some <b>for<i>mat</i>t</b>ing</p>
</div>
<script type="text/javascript">
function highlight(text) {
let colour = "#ff0";
unhighlight(document.body, 'ffff00');
while (window.find(text)) {
var range, sel;
if (window.getSelection) {
// IE9 and non-IE
try {
if (!document.execCommand("BackColor", false, colour)) {
makeEditableAndHighlight(colour);
}
} catch (ex) {
makeEditableAndHighlight(colour)
}
} else if (document.selection && document.selection.createRange) {
// IE <= 8 case
range = document.selection.createRange();
range.execCommand("BackColor", false, colour);
}
}
}
function makeEditableAndHighlight(colour) {
var range, sel = window.getSelection();
if (sel.rangeCount && sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
// Use HiliteColor since some browsers apply BackColor to the whole block
if (!document.execCommand("HiliteColor", false, colour)) {
document.execCommand("BackColor", false, colour);
}
document.designMode = "off";
}
function unhighlight(node, colour) {
if (node.nodeType == 1) {
var bg = node.style.backgroundColor;
node.style.backgroundColor = "";
}
var child = node.firstChild;
while (child) {
unhighlight(child, colour);
child = child.nextSibling;
}
}
</script>
</body>
</html>
I am trying to implement a Search Over a JqxTree in which i am populating data with the help of JSON.
I want to implement the Search in a way that when i enter a string in a textbox the tree should expand till that component.
Can anyone help me out with this.
Following is my jsp code:-
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/jqwidgets/styles/jqx.base.css" type="text/css" />
<script type="text/javascript" src="<%=request.getContextPath()%>/scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/scripts/demos.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jqwidgets/jqxcore.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jqwidgets/jqxdata.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jqwidgets/jqxbuttons.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jqwidgets/jqxscrollbar.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jqwidgets/jqxpanel.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jqwidgets/jqxtree.js"></script>
</head>
<body>
<div id='content' style='float: right;'>
<script type="text/javascript">
$(document).ready(function () {
$('#ExpandAll').jqxButton({ height: '25px', width: '100px'});
$('#CollapseAll').jqxButton({ height: '25px', width: '100px'});
// Expand All
$('#ExpandAll').click(function () {
$('#jqxWidget').jqxTree('expandAll');
});
//Collapse All
$('#CollapseAll').click(function () {
$('#jqxWidget').jqxTree('collapseAll');
});
var data = <%=request.getAttribute("data")%>
// prepare the data
var source =
{
datatype: "json",
datafields: [
{ name: 'categoryId' },
{ name: 'parentId' },
{ name: 'categoryName' },
],
id: 'categoryId',
localdata: data
};
// create data adapter.
var dataAdapter = new $.jqx.dataAdapter(source);
// perform Data Binding.
dataAdapter.dataBind();
// Get the tree items.
//The 1st parameter is the item's id.
//The 2nd parameter is the parent item's id.
//The 'items' parameter represents the sub items collection name.
//Each jqxTree item has a 'label' property, but in the JSON data, we have a 'text' field.
//The last parameter specifies the mapping between the 'text' and 'label' fields.
var records = dataAdapter.getRecordsHierarchy('categoryId', 'parentId', 'items', [{ name: 'categoryName', map: 'label'}]);
$('#jqxWidget').jqxTree({ source: records, width: '500px'});
});
</script>
</div>
<!-- DIV COMPONENTS -->
<div style='margin-top: 10px;'>
<input type="button" id='ExpandAll' value="Expand All" />
</div>
<div style='margin-top: 10px;' >
<input type="button" id='CollapseAll' value="Collapse All" />
</div><br/>
<div id='jqxWidget'>
</div>
</body>
</html>
Please Help me out..!! :)
Here's how I achieved It
$("#btnSearchTree").on('click', function () {
//Setting current selected item as null
$('#jqxWidget').jqxTree('selectItem', null);
//collapsing tree(in case if user has already searched it )
$('#jqxWidget').jqxTree('collapseAll');
//Using span for highlighting text so finding earlier searched items(if any)
var previousHighlightedItems = $('#jqxWidget').find('span.highlightedText');
// If there are some highlighted items, replace the span with its html part, e.g. if earlier it was <span style="background-color:"Yellow">Te></span>st then it will replace it with "Te""st"
if (previousHighlightedItems && previousHighlightedItems.length > 0) {
var highlightedText = previousHighlightedItems.eq(0).html();
$.each(previousHighlightedItems, function (idx, ele) {
$(ele).replaceWith(highlightedText);
});
}
//Getting all items for jqxTree
var items = $('#jqxWidget').jqxTree("getItems");
//Getting value for input search box and converting it to lower for case insensitive(may change)
var searchedValue = $("#ipSearchTreeText").val().toLowerCase();
//Searching the text in items label
for (var i = 0; i < items.length; i++) {
if (items[i].label.toLowerCase().indexOf(searchedValue) > -1) {
//If found expanding the tree to that item
$('#jqxWidget').jqxTree('expandItem', items[i].parentElement);
//selecting the item : not necessary as it selects the last item if multiple occurrences are found
$('#jqxWidget').jqxTree('selectItem', items[i]);
//changing the innerhtml of found item and adding span with highlighted color
var itemLabelHTML = $(items[i].element).find('div').eq(0).html();
//splitting the item text so that only searched text
can be highlighted by appending span to it.
var splittedArray = itemLabelHTML.split(searchedValue);
var highlightedText = '';
//if there are multiple occurrences of same searched text then adding span accordingly
for (var j = 0; j < splittedArray.length; j++) {
if (j != splittedArray.length - 1)
highlightedText = highlightedText + splittedArray[j] + '<span class="highlightedText" style="background-color:yellow">' + searchedValue + '</span>';
else
highlightedText = highlightedText + splittedArray[j];
}
//var highlightedText = splittedArray[0] + '<span style="background-color:yellow">' + searchedValue + '</span>' + splittedArray[1];
//replacing the item html with appended styled span
$(items[i].element).find('div').eq(0).html(highlightedText);
}
};
});
I have Google Autocomplete set up for a text field of an HTML form, and it's working perfectly.
However, when the list of suggestions appear, and you use the arrows to scroll and select using enter, it submits the form, though there are still boxes to fill in. If you click to select a suggestion it works fine, but pressing enter submits.
How can I control this? How can I stop enter from submitting the form, and instead be the selection of a suggestion from autocomplete?
Thanks!
{S}
You can use preventDefault to stop the form being submitted when enter is hit, I used something like this:
var input = document.getElementById('inputId');
google.maps.event.addDomListener(input, 'keydown', function(event) {
if (event.keyCode === 13) {
event.preventDefault();
}
});
Using the Google events handling seems like the proper solution but it's not working for me. This jQuery solution is working for me:
$('#inputId').keydown(function (e) {
if (e.which == 13 && $('.pac-container:visible').length) return false;
});
.pac-container is the div that holds the Autocomplete matches. The idea is that when the matches are visible, the Enter key will just choose the active match. But when the matches are hidden (i.e. a place has been chosen) it will submit the form.
I've amalgamated the first two answers from #sren and #mmalone to produce this:
var input= document.getElementById('inputId');
google.maps.event.addDomListener(input, 'keydown', function(e) {
if (e.keyCode == 13 && $('.pac-container:visible').length) {
e.preventDefault();
}
});
works perfectly on the page. prevents the form from being submitted when the suggestion container (.pac-container) is visible. So now, an option from the autocomplete dropdown is selected when the users presses the enter key, and they have to press it again to submit the form.
My main reason for using this workaround is because I found that if the form is sent as soon as an option is selected, via the enter key, the latitude and longitude values were not being passed fast enough into their hidden form elements.
All credit to the original answers.
This one worked for me:
google.maps.event.addDomListener(input, 'keydown', e => {
// If it's Enter
if (e.keyCode === 13) {
// Select all Google's dropdown DOM nodes (can be multiple)
const googleDOMNodes = document.getElementsByClassName('pac-container');
// Check if any of them are visible (using ES6 here for conciseness)
const googleDOMNodeIsVisible = (
Array.from(googleDOMNodes).some(node => node.offsetParent !== null)
);
// If one is visible - preventDefault
if (googleDOMNodeIsVisible) e.preventDefault();
}
});
Can be easily converted from ES6 to any browser-compatible code.
The problem I had with #sren's answer was that it blocks the submit event always. I liked #mmalone's answer but it behaved randomly, as in sometimes when I hit ENTER to select the location, the handler ran after the container is hidden. So, here's what I ended up doing
var location_being_changed,
input = document.getElementById("js-my-input"),
autocomplete = new google.maps.places.Autocomplete(input),
onPlaceChange = function () {
location_being_changed = false;
};
google.maps.event.addListener( this.autocomplete,
'place_changed',
onPlaceChange );
google.maps.event.addDomListener(input, 'keydown', function (e) {
if (e.keyCode === 13) {
if (location_being_changed) {
e.preventDefault();
e.stopPropagation();
}
} else {
// means the user is probably typing
location_being_changed = true;
}
});
// Form Submit Handler
$('.js-my-form').on('submit', function (e) {
e.preventDefault();
$('.js-display').text("Yay form got submitted");
});
<p class="js-display"></p>
<form class="js-my-form">
<input type="text" id="js-my-input" />
<button type="submit">Submit</button>
</form>
<!-- External Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="//maps.googleapis.com/maps/api/js?sensor=false&libraries=places"></script>
The flag ensures that if the location is being changed & user hits enter, the event is blocked. Eventually the flag is set to false by google map's place_changed event, which then allows the form to be submitted on hitting the enter key.
Here's a simple code that worked well for me (uses no jquery).
const googleAutcompleteField = this.renderer.selectRootElement(this.elem.nativeElement);
this.selectOnEnter(googleAutcompleteField);
This piece of code, to follow the code above, is used to implement google maps autocomplete (with or without the Enter key functionality sought in this question):
this.autocomplete = new google.maps.places.Autocomplete(googleAutcompleteField, this.googleMapsOptions);
this.autocomplete.setFields(['address_component', 'formatted_address', 'geometry']);
this.autocomplete.addListener('place_changed', () => {
this.zone.run(() => {
this.googleMapsData.emit([this.autocomplete.getPlace()]);
})
})
selectOnEnter (called above in the first piece of code) defined:
selectOnEnter(inputField) {
inputField.addEventListener("keydown", (event) => {
const selectedItem = document.getElementsByClassName('pac-item-selected');
if (event.key == "Enter" && selectedItem.length != 0) {
event.preventDefault();
}
})
}
This code makes the google maps autocomplete field select whichever item user selects with the down arrow keypress. Once user selects an option with a press of the Enter key, nothing happens. User has to press Enter again to trigger onSubmit() or other command
You can do it in vanilla :
element.addEventListener('keydown', function(e) {
const gPlaceChoices = document.querySelector('.pac-container')
// No choices element ?
if (null === gPlaceChoices) {
return
}
// Get choices visivility
let visibility = window.getComputedStyle(gPlaceChoices).display
// In this case, enter key will do nothing
if ('none' !== visibility && e.keyCode === 13) {
e.preventDefault();
}
})
I tweaked Alex's code, because it broke in the browser. This works perfect for me:
google.maps.event.addDomListener(
document.getElementById('YOUR_ELEMENT_ID'),
'keydown',
function(e) {
// If it's Enter
if (e.keyCode === 13) {
// Select all Google's dropdown DOM nodes (can be multiple)
const googleDOMNodes = document.getElementsByClassName('pac-container');
//If multiple nodes, prevent form submit.
if (googleDOMNodes.length > 0){
e.preventDefault();
}
//Remove Google's drop down elements, so that future form submit requests work.
removeElementsByClass('pac-container');
}
}
);
function removeElementsByClass(className){
var elements = document.getElementsByClassName(className);
while(elements.length > 0){
elements[0].parentNode.removeChild(elements[0]);
}
}
I've tried the above short answers but they didn't work for me, and the long answers I didn't want to try them, so I've created the following code which worked pretty well for me. See Demo
Suppose this is your form:
<form action="" method="">
<input type="text" name="place" id="google-places-searchbox" placeholder="Enter place name"><br><br>
<input type="text" name="field-1" placeholder="Field 1"><br><br>
<input type="text" name="field-2" placeholder="Field 2"><br><br>
<button type="submit">Submit</button>
</form>
Then the following javascript code will solve the problem:
var placesSearchbox = $("#google-places-searchbox");
placesSearchbox.on("focus blur", function() {
$(this).closest("form").toggleClass('prevent_submit');
});
placesSearchbox.closest("form").on("submit", function(e) {
if (placesSearchbox.closest("form").hasClass('prevent_submit')) {
e.preventDefault();
return false;
}
});
And here is how the full code looks like in the HTML page (Note that you need to replace the YOUR_API_KEY with your google api key):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Prevent form submission when choosing a place from google places autocomplete searchbox</title>
</head>
<body>
<form action="" method="">
<input type="text" name="place" id="google-places-searchbox" placeholder="Enter place name"><br><br>
<input type="text" name="field-1" placeholder="Field 1"><br><br>
<input type="text" name="field-2" placeholder="Field 2"><br><br>
<button type="submit">Submit</button>
</form>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- Google Maps -->
<!-- Note that you need to replace the next YOUR_API_KEY with your api key -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"
async defer></script>
<script>
var input = document.getElementById("google-places-searchbox");
var searchBox = new google.maps.places.SearchBox(input);
var placesSearchbox = $("#google-places-searchbox");
placesSearchbox.on("focus blur", function() {
$(this).closest("form").toggleClass('prevent_submit');
});
placesSearchbox.closest("form").on("submit", function(e) {
if (placesSearchbox.closest("form").hasClass('prevent_submit')) {
e.preventDefault();
return false;
}
});
</script>
</body>
</html>
$("#myinput").on("keydown", function(e) {
if (e.which == 13) {
if($(".pac-item").length>0)
{
$(".pac-item-selected").trigger("click");
}
}
Use $('.pac-item:first').trigger('click'); if you want to select first result
I've removed pretty much all .js references in my Magento theme. Specifically I've removed the onclick from the add to cart button.
in template/catalog/product/view/addtocart.phtml I've removed onclick="productAddToCartForm.submit(this)
in template/catalog/product/view/view.phtml I removed this block of code...
<script type="text/javascript">
//<![CDATA[
var productAddToCartForm = new VarienForm('product_addtocart_form');
productAddToCartForm.submit = function(button, url) {
if (this.validator.validate()) {
var form = this.form;
var oldUrl = form.action;
if (url) {
form.action = url;
}
var e = null;
try {
this.form.submit();
} catch (e) {
}
this.form.action = oldUrl;
if (e) {
throw e;
}
if (button && button != 'undefined') {
button.disabled = true;
}
}
}.bind(productAddToCartForm);
productAddToCartForm.submitLight = function(button, url){
if(this.validator) {
var nv = Validation.methods;
delete Validation.methods['required-entry'];
delete Validation.methods['validate-one-required'];
delete Validation.methods['validate-one-required-by-name'];
if (this.validator.validate()) {
if (url) {
this.form.action = url;
}
this.form.submit();
}
Object.extend(Validation.methods, nv);
}
}.bind(productAddToCartForm);
//]]>
</script>
However, now when I submit the form I get nothing.
I figured to change the add to cart <button> to a proper submit. So I changed this....
<button type="button" title="<?php echo $buttonTitle ?>" class="button btn-cart"><span><span><?php echo $buttonTitle ?></span></span></button>
to this ...
<input type="submit" value="<?php echo $buttonTitle ?>" />
When I do that, the form submits but I get a "Page Not Found", the URL it takes me to looks like this /checkout/cart/add/uenc/aHR0cDovLzcwLjMyLjc0LjQ2L3J0bF9tYWdlbnRvL2luZGV4LnBocC9jYXRhbG9nL3Byb2R1Y3Qvdmlldy9pZC8xNQ,,/product/15/
Is it not possible to submit a form the old fashioned way in Magento without javascript? If it is, can you give some pointers?
My plan was to hook up my own jQuery validation (which is quite simple, I just need to validate that the qty field has length) and ditch some of the ridiculousness of the code above.
I don't know how and I don't know why, but when I disable "Use Web Server Rewrites" it works with the standard submit button.