WebSockets on iOS - iphone

I've read that WebSockets work on iOS 4.2 and above. And I can verify that there is indeed a WebSocket object. But I can't find a single working WebSocket example that works on the phone.
For example http://yaws.hyber.org/websockets_example.yaws will crash the Mobile Safari app. Has anyone got WebSockets working successfully on the phone?

I may have found the solution. Mobile Safari only crashes with websockets when you have setup a proxy over wifi.

It is supported, but bear in mind regarding the standard that iOS Safari browser implements, it is not RFC 6455, but HyBi-00/Hixie-76.
You can test as well using this browser: http://websocketstest.com/
As well check this great post that have most of info regarding versions: https://stackoverflow.com/a/2700609/1312722
OBS!, this is an old answer.
I have checked through the webpage mentioned in this post combined with browserstack.com:
iPhone4S
iPhone5
iPhone5S
iPhone6
iPhone6 Plus
iPhone6S
iPhone6S Plus
All using RFC 6455

I had a similar problem and even looked to this post to find a fix for it. For me, it had nothing to do with being on a wifi connection. It appears to be a bug in the iOS implementation of websockets (even up to the current version 5.1). Turning on a bunch of XCode's debugging I found that it has something to do with memory management because I would get something along the lines of "message sent to a deallocated instance." Most likely there was an object that didn't have the correct reference count and was cleaned up way too early.
This blog has a lot of great information about the symptoms of the problem and how to debug it, but doesn't have a workaround: http://dalelane.co.uk/blog/?p=1652
Eventually though, I found this workaround, and my app has almost entirely stopped crashing now.
me = this // strange javascript convention
this.socket = new WebSocket(url);
// put onmessage function in setTimeout to get around ios websocket crash
this.socket.onmessage = function(evt) { setTimeout(function() {me.onMessageHandler(evt);}, 0); };

I got them working on Chrome and Safari, iPhone and iPad (and other mobile devices too, but I guess you don't mind about them). Here is the Javascript code I am using :
<script language="javascript" type="text/javascript">
var wsUri = document.URL.replace("http", "ws");
var output;
var websocket;
function init()
{
output = document.getElementById("output");
wsConnect();
}
function wsConnect()
{
console.log("Trying connection to " + wsUri);
try
{
output = document.getElementById("output");
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt)
{
onOpen(evt)
};
websocket.onclose = function(evt)
{
onClose(evt)
};
websocket.onmessage = function(evt)
{
onMessage(evt)
};
websocket.onerror = function(evt)
{
onError(evt)
};
}
catch (e)
{
console.log("Exception " + e.toString());
}
}
function onOpen(evt)
{
alert("Connected to " + wsUri);
}
function onClose(evt)
{
alert("Disconnected");
}
function onMessage(evt)
{
alert('Received message : ' + evt.data);
}
function onError(evt)
{
alert("Error : " + evt.toString());
}
function doSend(message)
{
websocket.send(message);
}
window.addEventListener("load", init, false);
Sending data from client to server is done calling doSend() function. Receiving data from server also works, I've tested it from a custom C++ server.

Here is a working sample
Web socket client
<!DOCTYPE html>
<meta charset="utf-8" />
<head>
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
var websocket;
function OpenWebSocket()
{
try {
websocket = new WebSocket(document.getElementById("wsURL").value);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
catch(err) {
writeToScreen(err.message);
}
}
function CloseWebSocket()
{
websocket.close();
}
function FindWebSocketStatus()
{
try {
if (websocket.readyState == 1){
writeToScreen("Websocket connection is in open state")
}
else if (websocket.readyState == 0){
writeToScreen("Websocket connection is in connecting state")
}
else{
writeToScreen("Websocket connection is in close state")
}
}
catch(err) {
writeToScreen(err.message);
}
}
function FindWebSocketBufferedAmount(){
try {
writeToScreen(websocket.bufferedAmount)
}
catch(err) {
writeToScreen(err.message);
}
}
function SendMessageThroughSocket(){
doSend(document.getElementById("wsMessage").value);
}
function onOpen(evt)
{
writeToScreen("Socket Connection Opened");
}
function onClose(evt)
{
writeToScreen("Socket Connection Closed");
}
function onMessage(evt)
{
writeToScreen('<span style="color: blue;">SERVER RESPONSE: ' + evt.data+'</span>');
}
function onError(evt)
{
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message)
{
try{
writeToScreen("CLIENT SENT: " + message);
websocket.send(message);
}
catch(err) {
writeToScreen(err.message);
}
}
function writeToScreen(message)
{
var output = document.getElementById("output");
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
</script>
</title>
</head>
<body>
<table>
<tr>
<td>
WebSocket URL
</td>
<td>
<input type="text" id="wsURL" value="ws://echo.websocket.org/"/>
</td>
</tr>
<tr>
<td>
WebSocket Message
</td>
<td>
<input type="text" id="wsMessage" value="Hi"/>
</td>
</tr>
<tr>
<td colspan="2" style="text-align:left;">
<input type="button" value="Open Socket Connection" onclick="OpenWebSocket();"/>
</td>
</tr>
<tr>
<td colspan="2" style="text-align:left;">
<input type="button" value="Send Message" onclick="SendMessageThroughSocket();"/>
</td>
</tr>
<tr>
<td colspan="2" style="text-align:left;">
<input type="button" value="Close Socket Connection" onclick="CloseWebSocket();"/>
</td>
</tr>
<tr>
<td colspan="2" style="text-align:left;">
<input type="button" value="Find Socket Status" onclick="FindWebSocketStatus();"/>
</td>
</tr>
<tr>
<td colspan="2" style="text-align:left;">
<input type="button" value="Find Socket Buffered Amount" onclick="FindWebSocketBufferedAmount();"/>
</td>
</tr>
</table>
<div id="output"></div>
</body>
</html>
Web Socket server
Creating your own socket server is also simple Just install the Node.js and socket.io then proceed to install web socket via npm
#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(8888, function() {
console.log((new Date()) + ' Server is listening on port 8888');
});
wsServer = new WebSocketServer({
httpServer: server,
// You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser. You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
});
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
var connection = request.accept();
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
connection.sendUTF('Message received at server:'+message.utf8Data);
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
}
});
connection.on('close', function(reasonCode, description) {
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
});
});
save the above file as .js and run it like node filename.js from terminal or command prompt
The above file is like we have first created a http server using node then we're passing the created http server instance to Websocketserver then subsequently to Socket.iO instance

I was debugging a similar issue and found that if you have used https to get the web page iOS will trap if you use the pass a the "ws:" protocol into WebSocket. If you use "wss:" everything will work and there will be no traps.

Related

ReferenceError: " postIts.forEach(function(postit)" on list.ejs? postIts is not defined at eval (eval at compile

I need to loop through an object PostIts and display the "Id", " Title" with an ejs "forEach" Loop Am using sails.js "1.2.3" and mongodb on local host, but i get error
ReferenceError : postIts is not defined at eval (eval at compile ?
Here is the code on the PostItsController.js:
module.exports = {
list: function(req, res) {
// res.view('list');
PostIts.find({}).exec(function(err, postIts) {
if (err) {
res.send(500, { error: 'Database Error' });
}
res.view('list', { postIts: postIts });
});
}
};
And here is the code on list.ejs:
<tbody>
<% postIts.forEach(function(postit){ %>
<tr>
<td>
<%= postit.id %>
</td>
<td>
<%= postit.title %>
</td>
<td></td>
</tr>
<% }) %>
</tbody>
I should get the value of the ID and title displayed on the list.ejs page in a table, but instead I get an error that the postIts object is not defined.
First of all your route '/postIts/list': { view: 'list' }, should point to an action (since it has backend logic) not a view, so in your case "/postIts/list": "PostItsController.list", but if you're using actions2 things would be simpler
Secondly you don't need to tell your users that you have a database error error: "Database Error"
Using Actions2
sails generate action post/list
In your config/route.js
'POST /api/v1/post/list': { action: 'post/list' },
In your action
module.exports = {
friendlyName: "List Posts",
description: "List all post in our site",
inputs: {},
exits: {
success: {
description: "The path to your template file",
viewTemplatePath: "list"
}
},
fn: async function(inputs) {
var posts = await Post.find();
// All done.
return { postIts: posts };
}
};
postit works
An Boohoo! it works
https://sailsjs.com/documentation/concepts/actions-and-controllers/routing-to-actions
If you're sure that res.view('list', { postIts: postIts }); is actually sending the correct data you can use _.each(postIts, cb()) ... instead
For some reason the postIts object didnt save the data from the post req I made instead it just recalled what I posted. and I used the '_.each(postIts, function (postit)' and it finally worked.
to me its like a magic happened hahaha but yeah I learned from it.
thanks #Navicstein Rotciv for the quick replies.

Receive Files in Drive and send Confirmation Email

I would like to create a form that allows anyone to upload multiple files to my drop box. But I would also like for the person that uploads the file to receive a confirmation email. The following code uploads the files but does send the email. Please help!
Here is the .gs
function doGet(e) {return HtmlService.createHtmlOutputFromFile('form.html');}
function sendEmails(form) {
var message= ", your abstract was submitted. Send other work by APR 20";
var subject = "Application Received";
MailApp.sendEmail(form.myEmail, subject, message);
}
function uploadFiles(form) {
try {
var dropbox = "New Abstracts";
var folder, folders = DriveApp.getFoldersByName(dropbox);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropbox);
}
var blob = form.myFile1;
var file = folder.createFile(blob);
file.setDescription("Uploaded by " + form.myName);
var blob = form.myFile2;
var file = folder.createFile(blob);
file.setDescription("Uploaded by " + form.myName);
return "File uploaded successfully " + file.getUrl();
} catch (error) {
return error.toString();
}
}
And here is the .html portion
<form id="myForm">
<input type="text" name="myName" placeholder="Your name..">
<input type="email" name="myEmail" placeholder ="Your email..">
<input type="file" name="myFile1" >
<input type="file" name="myFile2" >
<input type="submit" value="Upload File"
onclick="this.value='Uploading..';
google.script.run.withSuccessHandler(fileUploaded)
.uploadFiles(this.parentNode).sendEmails(this.parentNode);
return false;">
<script>
function fileUploaded(status) {
document.getElementById('myForm').style.display = 'none';
document.getElementById('output').innerHTML = status;
}
</script>
<style>
input { display:block; margin: 20px; }
</style>
I trying this based on the codes found at:
http://www.labnol.org/internet/receive-files-in-google-drive/19697/
You can't append multiple calls to the script like this:
google.script.run.withSuccessHandler(fileUploaded)
.uploadFiles(this.parentNode).sendEmails(this.parentNode);
Only uploadFiles (the first one being called) gets executed. Everything after that is getting ignored.
You can add the code inside sendEmails() to the uploadFiles() as one solution? Or add it to the success handler? Or have uploadFiles() call sendEmails() once it is done...

ejabberd XMPP connection failed using strophejs

I am trying to run THIS XMPP JS code using ejabberd on server side. Here is my connection code:
$(document).bind('connect', function (ev, data) {
console.log("connect "+JSON.stringify(data));
var conn = new Strophe.Connection("http://localhost:5280/http-bind");
conn.connect(data.jid, data.password, function (status) {
console.log("status"+status);
if (status === Strophe.Status.CONNECTED) {
$(document).trigger('connected');
} else if (status === Strophe.Status.DISCONNECTED) {
$(document).trigger('disconnected');
}
});
Hello.connection = conn;
});
I am getting status = 1. and after few time later it starts keep showing :
Uncaught Error: 11, flXHR::abort(): Failed, The abort() call failed to complete.
My ejabberd is running on http://localhost:5280/admin/ and admin#ejab is admin user.
Here is full code:
<!DOCTYPE html>
<html>
<head>
<title>Strophe.js Basic Example</title>
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js'></script>
<script src='../strophe.js'></script>
<!-- <script src='scripts/flXHR.js'></script>
<script src='scripts/strophe.flxhr.js'></script>-->
<script src='basic.js'></script>
</head>
<body>
<div id='login' style='text-align: center'>
<form name='cred'>
<label for='jid'>JID:</label>
<input type='text' id='jid'>
<label for='pass'>Password:</label>
<input type='password' id='pass'>
<input type='button' id='connect' value='connect'>
</form>
</div>
<hr>
<div id='log'></div>
</body>
</html>
basic.js
var BOSH_SERVICE = 'http://localhost:5222/http-bind';
var connection = null;
function log(msg)
{
$('#log').append('<div></div>').append(document.createTextNode(msg));
}
function rawInput(data)
{
log('RECV: ' + data);
}
function rawOutput(data)
{
log('SENT: ' + data);
}
function onConnect(status)
{
if (status == Strophe.Status.CONNECTING) {
log('Strophe is connecting.');
} else if (status == Strophe.Status.CONNFAIL) {
log('Strophe failed to connect.');
$('#connect').get(0).value = 'connect';
} else if (status == Strophe.Status.DISCONNECTING) {
log('Strophe is disconnecting.');
} else if (status == Strophe.Status.DISCONNECTED) {
log('Strophe is disconnected.');
$('#connect').get(0).value = 'connect';
} else if (status == Strophe.Status.CONNECTED) {
log('Strophe is connected.');
connection.disconnect();
}
}
$(document).ready(function () {
connection = new Strophe.Connection(BOSH_SERVICE);
connection.rawInput = rawInput;
connection.rawOutput = rawOutput;
$('#connect').bind('click', function () {
var button = $('#connect').get(0);
if (button.value == 'connect') {
button.value = 'disconnect';
connection.connect($('#jid').get(0).value,
$('#pass').get(0).value,
onConnect);
} else {
button.value = 'connect';
connection.disconnect();
}
});
});
ouput is :
Strophe is connecting.
SENT: <body rid='745165534' xmlns='http://jabber.org/protocol/httpbind' to='ejab' xml:lang='en' wait='60' hold='1' content='text/xml; charset=utf-8' ver='1.6' xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh'/>
status = 1 means connecting. It mostly means BOSH server cannot be reached.
Type in
http://localhost:5280/http-bind/
at Chrome to see if you can really reach the BOSH server.
If your server is really host at localhost (your computer where hello.html stored), you can comment out the
flXHR.js and strophe.flxhr.js at hello.html's header as they are for cross domain access.
e.g. hello.html at your desktop and using a domain(xmpp server) that host at another computer which are not allowed by Chrome due to cross domain access security already.
If both hello.html and xmpp server are located at the same computer, it won't have cross domain issue.
Anyway, these 2 files seem not able to overcome the cross domain access limit set by Chrome browser a few years back. Use Chrome's parameter "--disable-web-security" can provide cross domain access for test.
Lastly, at Chrome browser, right click to select "Inspect Element" -> Network, reload the hello.html and login to check those http-bind access. They should give hint on what goes wrong.

Auto complete with multiple keywords

I want . Auto complete text box with multiple keyword. it's from database. if I use jQuery and do operation in client side mean. If the database size is huge, it leads to some issues. I need to know how this is done on the server side and get proper result.
I have already seen this topic but the operation is done on the client side. I need it from the database directly.
<html>
<head>
<title>Testing</title>
<link href="css/jquery-ui-1.10.3.custom.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.srchHilite { background: yellow; }
</style>
<script src="scripts/jquery-1.9.1.min.js" type="text/javascript"></script>
<script src="scripts/jquery-ui-1.10.3.custom.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
NewAuto();
});
function NewAuto() {
var availableTags = ["win the day", "win the heart of", "win the heart of someone"];
alert(availableTags); // alert = win the day,win the heart of,win the heart of someone
$("#tags").autocomplete({
source: function(requestObj, responseFunc) {
var matchArry = availableTags.slice(); // Copy the array
var srchTerms = $.trim(requestObj.term).split(/\s+/);
// For each search term, remove non-matches.
$.each(srchTerms, function(J, term) {
var regX = new RegExp(term, "i");
matchArry = $.map(matchArry, function(item) {
return regX.test(item) ? item : null;
});
});
// Return the match results.
responseFunc(matchArry);
},
open: function(event, ui) {
// This function provides no hooks to the results list, so we have to trust the selector, for now.
var resultsList = $("ul.ui-autocomplete > li.ui-menu-item > a");
var srchTerm = $.trim($("#tags").val()).split(/\s+/).join('|');
// Loop through the results list and highlight the terms.
resultsList.each(function() {
var jThis = $(this);
var regX = new RegExp('(' + srchTerm + ')', "ig");
var oldTxt = jThis.text();
jThis.html(oldTxt.replace(regX, '<span class="srchHilite">$1</span>'));
});
}
});
}
</script>
</head>
<body>
<div>
<label for="tags">
Multi-word search:
</label>
<input type="text" id="tags" />
</div>
</body>
</html>
take a look to Select2 it may help you.
Select2 is a jQuery based replacement for select boxes. It supports
searching, remote data sets, and infinite scrolling of results.
link
In your code, you have provided source as array. As you mentioned in comments, problem is how to get the data to source in jquery.
To make this work,
You need to do following
load jquery in header, which is you have already done.
Provid array,string or function for the source tag. [See api for
the source tag][1]
[1]: http://api.jqueryui.com/autocomplete/#option-source
In your serverside script, provid Jason encoded string.
If you check the API, you can see they have clear mentioned this.
Here is the jquery code
$(function() {
$( "#option_val" ).autocomplete({
dataType: "json",
source: 'search.php',
minLength: 1,
select: function( event, ui ) {
log( ui.item ?
"Selected: " + ui.item.value + " aka " + ui.item.id :
"Nothing selected, input was " + this.value );
}
});
});
</script>
Here is the php code, Sorry if you use differnt serverside script language.
<?php
// Create connection
$con=mysqli_connect("localhost","wordpress","password","wordpress");
// Check connection
if (mysqli_connect_errno($con))
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$result=mysqli_query($con,"select * from wp_users");
while($row = mysqli_fetch_array($result))
{
$results[] = array('label' => $row['user_login']);
}
echo json_encode($results);
mysqli_close($con);
?>

Editing and deleting a newly added table row using Jquery

I'm adding new rows dynamically to the existing table, the first column of the table holds the Edit & Delete buttons. I'm facing 2 problems with this:
Not able to Edit and Delete newly added rows, tried .live but couldn't make it work
Not able to get the record id of the newly added rows (ajax returns the record when new rows are added).
Code looks like this:
Adding new rows:
<script type="text/javascript">
$(function() {
$('#btnSubmit').click(function() {
var oEmployee = new Object();
oEmployee.Title = $("#Title").val();
oEmployee.Middlename = $("#MiddleName").val();
oEmployee.Lastname = $("#LastName").val();
oEmployee.Email = $("#Email").val();
var DTO = {'employee': oEmployee};
var options = {
type: "POST",
url: "WebService.asmx/InsertEmployee",
data: JSON.stringify(DTO),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(response) {
if (response.d != "") {
if (parseInt(response.d) >= 1) {
var contactID;
contactID = parseInt(response.d);
$('#tblEmployee tbody tr:first').after("<tr id=" + contactID + "><td><input type='button' class='newContactID' value='Edit'/> <input type='button' value='Delete'/></td><td align=center>" + contactID + "</td><td align=center>" + oEmployee.Title + "</td><td align=center>" + oEmployee.Middlename + "</td><td align=center>" + oEmployee.Lastname + "</td><td align=center>" + oEmployee.Email + "</td><tr>"); // need to hook up editing and deleting function to the newly added rows }
else {
alert("Insert Failed \n" + response.d);
}
}
}
};
//Call the webservice
$.ajax(options);
});
});
</script>
Code for editing and deleting:
$(function() {
$("#tblEmployee > tbody > tr ").each(function() {
var TRID = $(this).attr("id");
$(this).find("td:first > input[value=Edit]").click(function() {
ResetOtherRowEdit();
ChangeTableCellToTextbox(TRID);
$(this).hide();
$("#tblEmployee > tbody > tr[id=" + TRID + "] > td:first> input[value=Delete]").hide();
return false;
});
$(this).find("td:first > input[value=Update]").click(function() {
UpdateRow(TRID);
});
$(this).find("td:first > input[value=Delete]").click(function() {
DeleteRow(TRID);
});
$(this).find("td:first > input[value=Cancel]").click(function() {
CancelEdit(TRID);
});
});
});
What is the best way to approach this? Editing and deleting of records work fine when they're pulled off the database.
Update
This is how the code looks like now, just began dabbling with Jquery a month back, still trying to get my head around it.
$(function() {
$("#tblEmployee > tbody > tr ").live('click', function(e) {
var TRID = $(this).attr("id");
var $target = $(e.target);
if ($target.is('#btnEdit')) {
$(this).find("td:first > input[value=Edit]").click(function() {
ResetOtherRowEdit();
ChangeTableCellToTextbox(TRID);
$(this).hide();
$("#tblEmployee > tbody > tr[id=" + TRID + "] > td:first> input[value=Delete]").hide();
return false;
});
}
else if ($target.is('#btnUpdate')) {
$(this).find("td:first > input[value=Update]").click(function() {
UpdateRow(TRID);
});
}
else if ($target.is('#btnCancel')) {
$(this).find("td:first > input[value=Cancel]").click(function() {
CancelEdit(TRID);
});
}
else if ($target.is('#btnDelete')) {
$(this).find("td:first > input[value=Delete]").click(function() {
DeleteRow(TRID);
});
}
});
});
HTML codes looks like this:
<ItemTemplate>
<tr id='<%# Eval("ContactID") %>'>
<td width="10%">
<input type="button" value="Edit" id="btnEdit"/>
<input type="button" value="Delete" id="btnDelete"/>
<input type="button" value="Update" style="display:none" id="btnUpdate" />
<input type="button" value="Cancel" style="display:none" id="btnCancel"/>
</td>
<td width="10%" align="center"><%# Eval("ContactID")%></td>
<td width="20%" align="center"><%# Eval("Title")%></td>
<td width="20%" align="center"><%# Eval("MiddleName")%></td>
<td width="20%" align="center"><%# Eval("LastName")%></td>
<td width="20%" align="center"><%# Eval("EmailAddress")%></td>
</tr>
</ItemTemplate>
You could take advantage of DOM traversal and .live() to make this work. Add a listener using .live() to the rows of the table. Inside this method, determine which element was clicked (e.currentTarget). You can use a simple conditional to check if it was a button that needs to react. Then, use DOM traversal to nail down what you want to have happen. For example, if e.currentTarget is the delete button, the you can use $(this).closest('tr').remove(); to delete the row. If you need to interact with the data through ajax, make your ajax function support passing in whatever valus you'd need (id) to perform the delete. In order to obtain the id, you'll need to get it from the ajax call and put it somewhere inside the DOM so you can retrieve it when you need it. You can always toss in a 'title' attribute when the tr is generated.
Here is a same script with php
http://www.amitpatil.me/add-edit-delete-rows-dynamically-using-jquery-php/
// Add new record
$(document).on("click","."+editbutton,function(){
var id = $(this).attr("id");
if(id && editing == 0 && tdediting == 0){
// hide editing row, for the time being
$("."+table+" tr:last-child").fadeOut("fast");
var html;
html += "<td>"+$("."+table+" tr[id="+id+"] td:first-child").html()+"</td>";
for(i=0;i<columns.length;i++){
// fetch value inside the TD and place as VALUE in input field
var val = $(document).find("."+table+" tr[id="+id+"] td[class='"+columns[i]+"']").html();
input = createInput(i,val);
html +='<td>'+input+'</td>';
}
html += '<td><img src="'+updateImage+'"> <img src="'+cancelImage+'"></td>';
// Before replacing the TR contents, make a copy so when user clicks on
trcopy = $("."+table+" tr[id="+id+"]").html();
$("."+table+" tr[id="+id+"]").html(html);
// set editing flag
editing = 1;
}
});