input value to reflect the value in javascript input value to reflect the value in javascript input value to reflect the value in javascript input value to reflect the value in javascript
<div id="goodContent{{ entity.id}}" onclick="copyToClipboard();" style="display:none;">
{{ pdf_yolu }}
</div>
<div class="btn btn-default" id="clickCopy">Kopyala</div>
document.getElementById("clickCopy").onclick = function() {
copyToClipboard(document.getElementById("goodContent{{ entity.id}}"));
};
function copyToClipboard(e) {
var tempItem = document.createElement('input');
tempItem.setAttribute('type','text');
tempItem.setAttribute('display','none');
let content = e;
if (e instanceof HTMLElement) {
content = e.innerHTML;
}
tempItem.setAttribute('value',content);
document.body.appendChild(tempItem);
tempItem.select();
document.execCommand('Copy');
tempItem.parentElement.removeChild(tempItem);
}
This is not a Symfony issue as Symfony does not execute its own code on web browsers, which is where you need "copy to clipboard" functionality to run. (You should remove the Symfony tag from your question.) This is entirely JavaScript.
Consider a library such as ClipboardJS to do this. I can confirm it is usable on the front end templates of a Symfony based project.
In my case I had a table with many rows needing the 2nd column of each row to be copyable with a button. I wanted the button to appear in the first column. Here's the script I wrote:
Bare in mind I use other front end libraries too such as sprintf, jQuery, jQuery UI, Bootstrap 4, and FontAwesome. You should be able to adapt this though.
jQuery(document).ready(function($){
$('table').find('tr.js-copyable').each(function(k, v){
var $tr = $(v).first();
if ($tr.length != 1) {
return;
}
var $th = $tr.find('th').first();
if ($th.length != 1) {
return;
}
var $td = $tr.find('td').first();
if ($td.length != 1) {
return;
}
var value = $td.text();
if (typeof value != "string" || (value = value.trim()).length < 1) {
return;
}
var tdTooltip = function(text, icon){
if (typeof text != "string" || (text = text.trim()).length < 1) {
return;
}
if (typeof icon != "string" || (icon = icon.trim()).length < 1) {
icon = "";
}
$td.tooltip('dispose');
$td.tooltip({
html: true,
title: sprintf("<i class=\"fa %s\"></i><span>%s</span>", icon, text),
trigger: "manual",
placement: "left",
});
$td.tooltip('show');
setTimeout(function(){
$td.tooltip('hide');
}, 2000);
};
var $copyButton = $('<button class="btn btn-info js-copy js-tooltip" title="Copy to clipboard"><i class="fa fa-copy"></i></button>');
var clipboard = new ClipboardJS($copyButton[0], {
text: function(trigger){
$copyButton.tooltip('hide');
return value;
},
});
clipboard.on('success', function(e){
tdTooltip("Copied!", "fa-check-circle");
});
clipboard.on('error', function(e){
tdTooltip("Failed to copy.", "fa-times-circle");
});
$copyButton.appendTo($th);
});
});
Not one bit of the above has any wiff of Symfony about it. It's all JavaScript and associated front end libraries.
Related
There are quite a few posts on this but after spending several hours trying to adapt the solutions that already exist, it's become clear to me that this is a little bit more complicated.
I'm attempting to create 'copy to clipboard' functionality so that our users can copy their serial number with a single click (I managed to achieve this functionality with 2 different solutions) but there's a major catch.
As the serial number is generated dynamically using a shortcode I cannot place it inside a HTML 'text'/'value' field, like so:
<input id="serial" value="[shortcode here]">
as this breaks the shortcode, so it has to be placed inside it's own div, which I have done like this:
<div id="serial">[shortcode here]</div>
This outputs the shortcode on the page with the direct parent ID of 'serial', so the JS I'm using should then select the text from the element ID - #serial.
Unfortunately it doesn't...
I've tried to adapt this method also with no luck:
From the Roll Your Own section: https://www.sitepoint.com/javascript-copy-to-clipboard/
This one was working with with plain text but not with the shortcode or custom div.
Can anyone provide me with a working clipboard solution as shown in the example above that won't break the shortcode?
document.getElementById("copyButton").addEventListener("click", function() {
copyToClipboardMsg(document.getElementById("copyTarget"), "msg");
});
document.getElementById("copyButton2").addEventListener("click", function() {
copyToClipboardMsg(document.getElementById("copyTarget2"), "msg");
});
document.getElementById("pasteTarget").addEventListener("mousedown", function() {
this.value = "";
});
function copyToClipboardMsg(elem, msgElem) {
var succeed = copyToClipboard(elem);
var msg;
if (!succeed) {
msg = "Copy not supported or blocked. Press Ctrl+c to copy."
} else {
msg = "Text copied to the clipboard."
}
if (typeof msgElem === "string") {
msgElem = document.getElementById(msgElem);
}
msgElem.innerHTML = msg;
setTimeout(function() {
msgElem.innerHTML = "";
}, 2000);
}
function copyToClipboard(elem) {
// create hidden text element, if it doesn't already exist
var targetId = "_hiddenCopyText_";
var isInput = elem.tagName === "INPUT" || elem.tagName === "TEXTAREA";
var origSelectionStart, origSelectionEnd;
if (isInput) {
// can just use the original source element for the selection and copy
target = elem;
origSelectionStart = elem.selectionStart;
origSelectionEnd = elem.selectionEnd;
} else {
// must use a temporary form element for the selection and copy
target = document.getElementById(targetId);
if (!target) {
var target = document.createElement("textarea");
target.style.position = "absolute";
target.style.left = "-9999px";
target.style.top = "0";
target.id = targetId;
document.body.appendChild(target);
}
target.textContent = elem.textContent;
}
// select the content
var currentFocus = document.activeElement;
target.focus();
target.setSelectionRange(0, target.value.length);
// copy the selection
var succeed;
try {
succeed = document.execCommand("copy");
} catch(e) {
succeed = false;
}
// restore original focus
if (currentFocus && typeof currentFocus.focus === "function") {
currentFocus.focus();
}
if (isInput) {
// restore prior selection
elem.setSelectionRange(origSelectionStart, origSelectionEnd);
} else {
// clear temporary content
target.textContent = "";
}
return succeed;
}
<input id="copyTarget" value="Some initial text"> <button id="copyButton">Copy</button><br><br>
<span id="copyTarget2">Some Other Text</span> <button id="copyButton2">Copy</button><br><br>
<input id="pasteTarget"> Click in this Field and hit Ctrl+V to see what is on clipboard<br><br>
<span id="msg"></span><br>
Would you please try above snippet?
Second #pruvik7373 answer
I think [shortcode] may contain some code which can translate into anything else so instead of using textContent use innerHTML it will make sure to copy everything as it is.
document.getElementById("copyButton").addEventListener("click", function() {
copyToClipboardMsg(document.getElementById("copyTarget"), "msg");
});
document.getElementById("copyButton2").addEventListener("click", function() {
copyToClipboardMsg(document.getElementById("serial"), "msg");
});
document.getElementById("pasteTarget").addEventListener("mousedown", function() {
this.value = "";
});
function copyToClipboardMsg(elem, msgElem) {
var succeed = copyToClipboard(elem);
var msg;
if (!succeed) {
msg = "Copy not supported or blocked. Press Ctrl+c to copy."
} else {
msg = "Text copied to the clipboard."
}
if (typeof msgElem === "string") {
msgElem = document.getElementById(msgElem);
}
msgElem.innerHTML = msg;
setTimeout(function() {
msgElem.innerHTML = "";
}, 2000);
}
function copyToClipboard(elem) {
// create hidden text element, if it doesn't already exist
var targetId = "_hiddenCopyText_";
var isInput = elem.tagName === "INPUT" || elem.tagName === "TEXTAREA";
var origSelectionStart, origSelectionEnd;
if (isInput) {
// can just use the original source element for the selection and copy
target = elem;
origSelectionStart = elem.selectionStart;
origSelectionEnd = elem.selectionEnd;
} else {
// must use a temporary form element for the selection and copy
target = document.getElementById(targetId);
if (!target) {
var target = document.createElement("textarea");
target.style.position = "absolute";
target.style.left = "-9999px";
target.style.top = "0";
target.id = targetId;
document.body.appendChild(target);
}
target.textContent = elem.innerHTML;
}
// select the content
var currentFocus = document.activeElement;
target.focus();
target.setSelectionRange(0, target.value.length);
// copy the selection
var succeed;
try {
succeed = document.execCommand("copy");
} catch(e) {
succeed = false;
}
// restore original focus
if (currentFocus && typeof currentFocus.focus === "function") {
currentFocus.focus();
}
if (isInput) {
// restore prior selection
elem.setSelectionRange(origSelectionStart, origSelectionEnd);
} else {
// clear temporary content
target.textContent = "";
}
return succeed;
}
<input id="copyTarget" value="Some initial text"> <button id="copyButton">Copy</button><br><br>
<div id="serial"><div>[pw_map address="New York City"</div> enablescrollwheel="false" key="YOUR API KEY"]</div> <button id="copyButton2">Copy</button><br><br>
<input id="pasteTarget"> Click in this Field and hit Ctrl+V to see what is on clipboard<br><br>
<span id="msg"></span><br>
Below code did the trick for me.
Note: Below snippet might not work as window object is not accessible.
$(function() {
$('#serial').click(function() {
window.clipboardData.setData('text', $(this).text());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="serial">[shortcode here]</div>
You can do it like:
<div id="serial">[shortcode here]</div>
<input id="some_other_id" type="text" />
And the javascript code:
//first get text from your div and put it inside input field
//then apply code to copy that text from input field
$(document).ready(function(){
var text = $("#serial").text();
$("#some_other_id").val(text);
//your code for copying text from input field goes here
});
I’m using angular.js to get some form data from some input fields. I need to build a feature that prevents a user from entering duplicate fields. So if a user entered duplicate fields I need alert the user with a alert box and than remove the duplicate. I know how to do this with jQuery, look at the code below. What is the most efficient way to achieve this with angular.js? Any help is greatly appreciated, I have template fiddle here to make it easier.
/** Handle Duplicate Barcodes **/
$(".alldifferent").on('keyup paste',function(){
var $this = $(this);
keyupDelay(function() {
var val = $this.val();
$this.attr('value', val);
if (val != '') {
var $dupes = $('input[value="' + val + '"]:gt(0)').val('');
if ($dupes.length > 0) alert('Error: Duplicates barcodes are not allowed!');
}
}, 300);
});
var keyupDelay = (function() {
var timer = 0;
return function(callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
Try ng-blur as a blur event fires when an element has lost focus.
If you can know the index at which the duplicate entry is being entered you can filter that item in the array and clear it.
$scope.check = function(val, index){
if(val !== '') {
for(var i = 0; i < $scope.num.length; i++ ){
var itm = $scope.num[i].text;
if(itm == val && i != index) {
$scope.num[index].text = '';
alert('Error: Duplicates barcodes are not allowed!');
break;
}
}
}
}
Working Plunker
Is there a recommended way to unittest javascript in Laravel 5? I'm using the jQuery framework to do the search autocomplete task (code below). There's a lot of if blocks that I want to test but I'm not sure how to best test everything in Laravel 5. Any help is appreciated.
<script type="text/javascript">
(function($) {
var autocomplete_timer;
var ajax_request;
var autocomplete_delay = 50;
var search_keyword = "";
var max_result_count = 5;
var autocomplete_results = $('#autocomplete-results');
var autocomplete_results_list;
var default_report_url = '{{ route('report_summary_date_range_filter') }}';
var selected_autocomplete_item_index = 0;
var active_li = false;
var autocomplete_error = $('#autocomplete-error');
var redirect_state = false; //
$(document).ready(function () {
autocomplete_results_list = autocomplete_results.children('ul');
// Search Autocomplete
$("#search").keyup(function (e) {
search_keyword = $(this).val();
// If there is an existing XHR, abort it.
if (ajax_request) {
ajax_request.abort()
}
// Enable list iteration via keyboard
if (e.keyCode == 40 || e.keyCode == 38) {
ajax_request.abort();
var results_count = autocomplete_results_list.children('li').length;
if (e.keyCode == 40 && selected_autocomplete_item_index < results_count && active_li != false) {
selected_autocomplete_item_index++;
} else if (e.keyCode == 38 && selected_autocomplete_item_index > 0 && active_li != false) {
selected_autocomplete_item_index--;
}
active_li = autocomplete_results_list.children('li:eq(' + selected_autocomplete_item_index + ')');
if (active_li.length > 0) {
active_li.addClass('active');
autocomplete_results_list.children('li').not(active_li).removeClass('active');
$('#search').val(active_li.children('.autocomplete-ticker').text());
}
e.preventDefault();
return false;
}
// Clear the timer so we don't end up with dupes.
clearTimeout(autocomplete_timer);
// don't trigger ajax if user pressed enter/return key
// while a redirect is triggered
if (e.keyCode == 13 && redirect_state == true) {
return false;
}
if (search_keyword != '') {
// reset the index
selected_autocomplete_item_index = 0;
active_li = false;
// assign timer a new timeout
autocomplete_timer = setTimeout(function() {
ajax_request = $.ajax({
type: 'POST',
url: '/ajax/company/search/' + search_keyword,
data: {'_token': '{{ csrf_token() }}'},
success: function(response) {
if (response.count != 0) {
autocomplete_results.show();
autocomplete_results_list.empty();
autocomplete_error.hide();
var current_results = ((response.count > max_result_count) ? max_result_count : response.count);
for (var index = 0; index < current_results; index++) {
autocomplete_results_list.append(
'<li>' +
'<span class="autocomplete-ticker">' + response.results[index].ticker + '</span>' +
'<span class="autocomplete-company">' + response.results[index].name + '</span>' +
'</li>'
);
}
} else {
autocomplete_results_list.empty();
autocomplete_results.show();
autocomplete_error.show();
}
}
});
}, autocomplete_delay);
} else {
autocomplete_results.hide();
autocomplete_results_list.empty();
}
}).keydown(function (e) {
// prevent moving cursor to start of input field
// if the user presses the up arrow key
if (e.keyCode == 38) {
e.preventDefault();
}
});
// handle user clicking of an autocomplete item
autocomplete_results_list.on('click', 'li', function () {
var ticker = $(this).children('.autocomplete-ticker').text();
$('#search').val(ticker);
default_report_url = default_report_url.replace('%7Bticker%7D', ticker);
// redirect
$(location).attr('href', default_report_url);
redirect_state = true;
});
// if the user presses the return key while an autocomplete list
// is present, select the first item on the list and trigger a redirect
$('#searchbar form').submit(function (e) {
if ($('#search').val() != '') {
if (autocomplete_results_list.has('li').length > 0) {
autocomplete_results_list.children('li').first().addClass('active');
var ticker = autocomplete_results_list.children('li').first()
.children('.autocomplete-ticker').text().toUpperCase();
if (ticker != '') {
default_report_url = default_report_url.replace('%7Bticker%7D', ticker);
// redirect
$(location).attr('href', default_report_url);
redirect_state = true;
}
}
}
e.preventDefault();
});
});
$(document).click(function (e) {
// Hide autocomplete results if user clicked outside the search input field
// or the autocomplete listing
var container = $('#searchbar');
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
$('#autocomplete-results').hide();
$('#autocomplete-results ul').empty();
}
});
})(jQuery);
</script>
The choice of Laravel (or any other back-end framework/language/platform) is entirely irrelevant. This is purely front-end code. As such, you need to be using front-end testing tools for it.
However, even before you start thinking about unit testing this code, you need to do some re-coding so that it is actually testable in units. As things stand, you have a large monolithic blob of code there which cannot really be unit tested at all. You need to extract the functionality into discrete functions; the shorter the better.
Once you've done that, I'd suggest that the best starting point for your testing would be QUnit. This is a unit test framework developed by the jQuery foundation, and used for testing jQuery itself. Since your code has jQuery as a dependency, I'd suggest that this would probably be the best place to start. However there are numerous other testing frameworks for JavaScript, and you may want to investigate some of those as well.
Bear in mind that UI code (which most front-end JavaScript is) is notoriously difficult to write good quality unit tests for. You may find that functional testing -- ie automated end-user testing via a browser -- will serve you better. (indeed, you should be considering doing this kind of testing, even if you do also write unit tests for the JS code). For this, you will need an automation tool for the browser. The most well known one is Selenium, but you may also want to look into Sahi and also PhantomJS.
I'm trying to make the TAB key navigate on my dGrid. I have used as a base the solution found at Dgrid set focus on cell, but there are a couple of issues I'm running into which I couldn't solve so far.
Below you can find the block I'm using now; Not all columns have editors, so for I added a var do the element definition to select the next column instead of doing a right. I also added support for SHIFT+TAB to make backwards navigation possible. MT4.prje.grids[gridId]is the dGrid instance. There might be various on the page.
The grid is created with
MT4.prje.grids[gridId] = new (declare([OnDemandGrid, Keyboard, Selection, CellSelection]))(gridInfo, gridId);
where gridInfo has the column definitions and the store. The store is created as:
new Observable(new Memory({'data': {}, 'idProperty': 'id'}));
The editors are usually TextBox, NumberTextBox and Select dijit widgets, all set to autoSave.
aspect.after(MT4.prje.grids[gridId], "edit", function (promise, cellNode) {
if (promise === null) return;
promise.then(function (widget) {
if (!widget._editorKeypressHandle) {
widget._editorKeypressHandle = on(widget, "keypress", function (e) {
for (var rowId in MT4.prje.grids[gridId].selection) {
break;
}
for (var columnId in MT4.prje.grids[gridId].selection[rowId]) {
break;
}
if (e.charOrCode == keys.TAB) {
e.preventDefault();
var cellToEdit = null,
cellEdited = MT4.prje.grids[gridId].cell(rowId, columnId);
if (e.shiftKey) {
if (cellEdited.column.previousEditor === undefined) {
rowId = parseInt(rowId) - 1;
if (MT4.prje.grids[gridId].row(rowId).element !== null) {
for (var lastColumnId in MT4.prje.grids[gridId].columns) {}
cellToEdit = MT4.prje.grids[gridId].cell(rowId, lastColumnId);
}
} else {
cellToEdit = MT4.prje.grids[gridId].cell(rowId, cellEdited.column.previousEditor);
}
} else {
if (cellEdited.column.nextEditor === undefined) {
var firstColumnId = null;
rowId = parseInt(rowId) + 1;
if (MT4.prje.grids[gridId].row(rowId).element === null) {
var fields = {};
for (var cId in MT4.prje.grids[gridId].columns) {
if ((cId != 'excluir') && (firstColumnId === null)) {
firstColumnId = cId;
}
fields[cId] = '';
}
MT4.prje.addRowToGrid(gridId, fields);
} else {
for (var cId in MT4.prje.grids[gridId].columns) {
if (cId != 'excluir') {
firstColumnId = cId;
break;
}
}
}
cellToEdit = MT4.prje.grids[gridId].cell(rowId, firstColumnId);
} else {
cellToEdit = MT4.prje.grids[gridId].cell(rowId, cellEdited.column.nextEditor);
}
}
if (cellToEdit) {
MT4.prje.grids[gridId].deselect(cellEdited);
MT4.prje.grids[gridId].select(cellToEdit);
MT4.prje.grids[gridId].edit(cellToEdit);
}
}
});
}
});
});
Even ignoring the new line part, there are a couple of errors that happen. First of all, the editor barely pops into existence and them disappears, together with the selection. Sometimes when tabbing to an empty column, the editor will be filled with the values of the previous editor. Is there a way to do it more consistently?
What I'm figuring is that there is a race condition happening on the sharedEditor (they are set to editOn: focus). I tried wrapping the deselect/select on a dojo.on('blur') and emit it. But that doesn't get consistently correct with the dijit/form/Select widgets. Is there a better event that I can call for it?
I also tried changing the final block to:
if (cellToEdit) {
on(cellToEdit.element, 'focus', function(){
MT4.prje.grids[gridId].select(cellToEdit);
});
on(cellEdited.element, 'blur', function(){
MT4.prje.grids[gridId].deselect(cellEdited);
on.emit(cellToEdit.element, 'focus', {'bubble': true, 'cancelable': false});
});
on.emit(cellEdited.element, 'blur', {'bubble': true, 'cancelable': false});
}
But that gives two errors:
If I do make changes to a cell it does not go to the next editor. Does not even select it.
The first time I move from an empty cell to another empty cell it doesn't work either.
Anyone got any ideas?
This fix works on dgrid 0.3.11.
Add to your dgrid's postCreate.
postCreate: function() {
var that = this;
this.inherited(arguments);
this.on('dgrid-datachange', function(evt) {
that._selectedCell = that.cell(evt);
});
aspect.after(this, 'save', function(dfd) {
dfd.then(function() {
var nextCell = that.right(that.cell(that._selectedCell.row.id, that._selectedCell.column.id));
that.edit(nextCell);
// Bonus Fix. Workaround dgrid bug that blocks field text to be selected on focus.
nextCell.element.widget && nextCell.element.widget.textbox && nextCell.element.widget.textbox.select();
});
});
}
I have the following structure:
<div id="campaignTags">
<div class="tags">Tag 1</div>
<div class="tags">Tag 2</div>
<div class="tags">Tag 3</div>
</div>
And I'm trying to match user input against the innerText of each children of #campaignTags
This is my latest attempt to match the nodes with user input jQuery code:
var value = "Tag 1";
$('#campaignTags').children().each(function(){
var $this = $(this);
if(value == $(this).context.innerText){
return;
}
The variable value is for demonstration purposes only.
A little bit more of context:
Each div.tags is added dynamically to div#campaignTags but I want to avoid duplicate values. In other words, if a user attempts to insert "Tag 1" once again, the function will exit.
Any help pointing to the right direction will be greatly appreciated!
EDIT
Here's a fiddle that I just created:
http://jsfiddle.net/TBzKf/2/
The lines related to this question are 153 - 155
I tried all the solutions, but the tag is still inserted, I guess it is because the return statement is just returning the latest function and the wrapper function.
Is there any way to work around this?
How about this:
var $taggedChild = $('#campaignTags').children().filter(function() {
return $(this).text() === value;
});
Here's a little demo, illustrating this approach in action:
But perhaps I'd use here an alternative approach, storing the tags within JS itself, and updating this hash when necessary. Something like this:
var $container = $('#campaignTags'),
$template = $('<div class="tags">'),
tagsUsed = {};
$.each($container.children(), function(_, el) {
tagsUsed[el.innerText || el.textContent] = true;
});
$('#tag').keyup(function(e) {
if (e.which === 13) {
var tag = $.trim(this.value);
if (! tagsUsed[tag]) {
$template.clone().text(tag).appendTo($container);
tagsUsed[tag] = true;
}
}
});
I used $.trim here for preprocessing the value, to prevent adding such tags as 'Tag 3 ', ' Tag 3' etc. With direct comparison ( === ) they would pass.
Demo.
I'd suggest:
$('#addTag').keyup(function (e) {
if (e.which === 13) {
var v = this.value,
exists = $('#campaignTags').children().filter(function () {
return $(this).text() === v;
}).length;
if (!exists) {
$('<div />', {
'class': 'tags',
'text': v
}).appendTo('#campaignTags');
}
}
});
JS Fiddle demo.
This is based on a number of assumptions, obviously:
You want to add unique new tags,
You want the user to enter the new tag in an input, and add on pressing enter
References:
appendTo().
filter().
keyup().
var value = "Tag 1";
$('#campaignTags').find('div.tags').each(function(){
if(value == $(this).text()){
alert('Please type something else');
}
});
you can user either .innerHTML or .text()
if(value === this.innerHTML){ // Pure JS
return;
}
OR
if(value === $this.text()){ // jQuery
return;
}
Not sure if it was a typo, but you were missing a close } and ). Use the jquery .text() method instead of innerText perhaps?
var value = "Tag 1";
$('#campaignTags').find(".tags").each(function(){
var content = $(this).text();
if(value === content){
return;
}
})
Here you go try this: Demo http://jsfiddle.net/3haLP/
Since most of the post above comes out with something here is another take on the solution :)
Also from my old answer: jquery - get text for element without children text
Hope it fits the need ':)' and add that justext function in your main customised Jquery lib
Code
jQuery.fn.justtext = function () {
return $(this).clone()
.children()
.remove()
.end()
.text();
};
$(document).ready(function () {
var value = "Tag 1";
$('#campaignTags').children().each(function () {
var $this = $(this);
if (value == $(this).justtext()) {
alert('Yep yo, return');)
return;
}
});
//
});