Link to home
Start Free TrialLog in
Avatar of Adebayo Ojo
Adebayo OjoFlag for Nigeria

asked on

Alternative solution to edit/save feature for a typical profile page.

Please I need any alternative solution to how to edit and save an entry on a typical profile page data. Below is my current code which works fine until when the text entry on any of my field contains an apostrophe. So in the code below, I'm using the text entry as the ID for the input element. Anything I click on the edit button to edit the entry, this line of my code brings up an editable field of the text -
document.getElementById(nameid).innerHTML = "<input type='text' value='" + name + "' id='" + txtnameid + "'>";

Open in new window

But instead of having the full text of - I'm a photographer in the editable field, I will only see the first letter of the string which is letter I
The remaining text don't show up, and when i click on the save button, I get the error - Uncaught TypeError: Cannot read property 'value' of null at save3 (functions.js:502)
This is because of the apostrophe in my text which I'm using as my input ID.
Is there any other way I can write this code that won't involve using text input as my ID? Thanks all.

<?php
$aboutme = 'I'm a photographer';
echo '<div id="name3<?php echo $aboutme; ?>"><?php echo $aboutme; ?> </div>';
echo '<input type="button" id="<?php echo $aboutme; ?>" name="<?php echo $aboutme; ?>" value="Edit" onclick="edit3(this.id)">';
echo '<input type="button" id="save3<?php echo $aboutme; ?>" class="saveBtn" name="<?php echo $aboutme; ?>" value="Save" style="visibility:hidden" onclick="save3(this.name);">';
?>

Open in new window


JavaScript
Function for edit button
function edit3(id) {
    nameid = "name3" + id;
    txtnameid = "txtname" + id;
    var name = document.getElementById(nameid).innerHTML;
    document.getElementById(nameid).innerHTML = "<input type='text' value='" + name + "' id='" + txtnameid + "'>";

    updateid = "save3" + id;
    
    document.getElementById(id).style.visibility = "hidden";
    document.getElementById(updateid).style.visibility = "visible";
    
}

Open in new window


Function for save button

function save3(id) {
    var nameid = "txtname" + id;
    var name = document.getElementById(nameid).value;  ----------------- LINE 502

    update_value3(id, name);

    document.getElementById(id).style.visibility = "visible";
    document.getElementById("save3" + id).style.visibility = "hidden";

    document.getElementById("name3" + id).innerHTML = name;
}

function update_value3(id, name) {
    var xhttp;
    if (window.XMLHttpRequest) {
        // code for modern browsers
        xhttp = new XMLHttpRequest();
    } else {
        // code for IE6, IE5
        xhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xhttp.open("GET", "functions/profile_page_func.php?id=" + id + "&name=" + name + "&status=Save", true);
    xhttp.send(null);
}

Open in new window

Avatar of Chad Haney
Chad Haney
Flag of United States of America image

Don't use string building for it. Use element building. Here is a snippet I have used in the past, but it does implement with jQuery.  Overall the concept is to bind to those with .editable as a class, unless they have a parent who has a class of .noEdit.  Then when they are clicked, a new dom element gets created as an input box, populated with the data from the clicked element.  Then when the input loses focus, it passes the original value off to a processing function as well as itself.  I have that function compare the data and submit it via ajax if different.  Then the new value is placed back into the clicked element, which destroys the input, since it was a child element.

Editable.InitEditField = function (vwInfo) {
    var $label = $(vwInfo);
    var original = $label.clone();
    var value = original.html().trim();
    var input = $("<input>");
    input.val(value);
    $label.removeClass("editable").addClass("editing");
    $label.html("").prepend(input);
    input.focus();
    $(input).focusout(function(){
       Editable.ProcessEditFieldResult(original,this);
    });
}
$(function() {
    $(document).off(('click.editable','.editable').on('click.editable','.editable',function(e){
        if(!$(this).parent().hasClass('noEdit')){
           Editable.InitEditField($(this));
        }
        e.preventDefault();
    });
});

Open in new window

Keep in mind that js / jquery can be manipulated so you will need to ultimately do this on the back end in php.

The key is to convert special characters to entities . When you display back to your site, the entities will show up as you expect ( &#8216; or &apos; will show up as a single quote http://jsbin.com/zaradijove/edit?html,output)

http://php.net/manual/en/function.htmlentities.php
greetings Adebayo Ojo, In order to avoid all STRING parsing for single and double quotes for the name variable, I created the input first by text insertion in the div, Then I got the child element of that div (the input) and changed the value without any quotes , just the name variable, this code works in firefox -

function edit3(id) {
    nameid = "name3" + id;
    txtnameid = "txtname" + id;
    var name = document.getElementById(nameid).innerHTML;
    var div0 = document.getElementById(nameid);
	div0.innerHTML = "<input type='text' id='" + txtnameid + "' />";
	div0.firstElementChild.value = name;
}

Open in new window


I can use this alert after the input insertion, and it gives me the value with any and all single or double quotes the div had in it.
    alert("value "+document.getElementById("txtname1").value);
Avatar of Adebayo Ojo

ASKER

Thanks for this Slick812. It works fine and now when I click on the edit button, the whole text is returned in the editable text field even with apostrophe.
Now the only issue is that when I click on the save button to save any change in the field, I still get that - Uncaught TypeError: Cannot read property 'value' of null
    at save3 (functions.js:3)


And this error occur only for any text input with an apostrophe, if the text is without apostrophe, the save function works perfectly.
When I inspected the element on the page for an input with and without an apostrophe, below is the result:

Without Apostrophe - Input text is
A developer in PHP
User generated image
With Apostrophe - Input text is
I'm a php programmer
User generated image
You can see how the apostrophe truncated the input ID and picks only the first letter of the ID instead of the whole text strings.

Below is also my save3 function:

function save3(id) {
    var nameid = "txtname" + id;
    var name = document.getElementById(nameid).value;

    update_value3(id, name);

    document.getElementById(id).style.visibility = "visible";
    document.getElementById("save3" + id).style.visibility = "hidden";

    document.getElementById("name3" + id).innerHTML = name;
}

function update_value3(id, name) {
    var xhttp;
    if (window.XMLHttpRequest) {
        // code for modern browsers
        xhttp = new XMLHttpRequest();
    } else {
        // code for IE6, IE5
        xhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xhttp.open("GET", "functions/profile_page_func.php?id=" + id + "&name=" + name + "&status=Save", true);
    xhttp.send(null);
    //console.log(status);
}

Open in new window


Please I need assistance on how to make sure the whole text strings are captured as the ID and not just the text before the apostrophe. Thanks for your help.
OK, I looked at the  code for the <div> and <input> you have in the images, and you have gone TOO FAR in shoving any and all TEXT into the element ID, , , , so you have the entire TEXT string from  "A Developer in PHP" forced into the ID like -
      id="txtnameA Developer in PHP"
AND you even try and force punctuation like the quote into an ID -
      id="txtnameI'm a php programmer"
which did not work. and did not render the quote or the rest of string.

In the HTML standards or specifications for element ID strings, it is recommended that all ID be alpha-numeric (just letters an numbers), with just the dash - and _ in an ID, That means NO spaces, and No punctuation or symbols, and especially NO QUOTES, single or double. However most browsers can handle some things like spaces, but some still will NOT handle a javascript ID with spaces in it? ?
In most code writes for elements, a reference string is combined with a Number, like your code here -
            nameid = "name3" + id;
            txtnameid = "txtname" + id;
where the variable id is just a number or a letter. But you now are assigning to the ID from a text string from the database it looks like, this is an incorrect thing to do, except if the strings can be Guaranteed to BE unique, and not have spaces or punctuation-symbols except the - and _
I do not see that you can test these strings like "A Developer in PHP" to be unique, before they go into the DB Table, although you could make the Table column "UNIQUE", but this unique is all unnecessary, since for programming you want to use short and sequential (0,1,2,3,4,5,6) ID strings, to Guarantee that all ID are unique, and will work in all browsers.

So to make these operations be more reliable to WORK, you need to change the way you assign the ID string, because -
      id="txtnameI'm a php programmer"
will NOT work, as you have seen.

= = = = = = = = = = = = = = = = =

This is not part of the question, but I looked at you code, and you do not seem to know how to treat AJAX URL parameters, because you have this-
xhttp.open("GET", "functions/profile_page_func.php?id=" + id + "&name=" + name + "&status=Save", true);

Open in new window

with the variable      + name +      added to the string WITHOUT preparing the variable + name + for proper URL syntax, if you are going to do AJAX, it is necessary to know how the  XMLHttpRequest object works and what does NOT work, to make sure you get correct URL parsing you should use the javascript  encodeURIComponent( ) for any variables from text sources that may have spaces, punctuation, symbols or other in them, maybe like -
xhttp.open("GET", "functions/profile_page_func.php?id=" 
     + id + "&name=" + encodeURIComponent(name) 
     + "&status=Save", true
    );

Open in new window

also, the XMLHttpRequest here is asynchronous so you likely should have a callback function assigned to the
        xhttp.onreadystatechange
so you can know if the HTTP request made it to the server and succeeded.
Just my opinion on this.
Thanks for your detail response.
The reason I did not use the user ID from the database is because the ID would not be unique for all the Bio fields for a particular user. For example, user A has an ID of 1 in the db. And I want to output and be able to edit each of the columns in the db table for this user, like firstname, lastname, Bio, Alias, etc on that user profile page. To make each of those column data unique is the major concern for me, and that's why I tried to use the text which I agree with you to be not a correct thing to do. But I know there must be a way to achieve this. Any idea would be appreciated.
Thanks once again Slick812.
Not sure what to say, I do not know the PHP LOOP code that you are using to assign these ID strings, What many do in loops is to have an increasing Number and a unique reference string for the ID

$num = 1;
while ($row = re->fetch($Whatever)) {
echo '<input type="text" id="inSet'.$num++.'" value="'.$row['name']."' />;;
}

but yours may be different?
Also, since the input Name has to be unique, many just use the same reference string for the name and id in an imput
    echo '<input type="text" name="'.$row['name'].'" id="'.$row['name'].'" value="'.$row['user']."' />;;
Thanks once again Slick812. Actually I'm just learning PHP. You've given me good clues and I would go and work with it and come back with my outcome. Thanks for your assistance.
OK, you say -
     "I want to output and be able to edit each of the columns in the db table for this user"

and In the PHP output you show here, the elements on the page code would look like this -
<div id="name3I'm a photographer">I'm a photographer </div>
<input type="button" id="I'm a photographer" name="I'm a photographer" value="Edit" onclick="edit3(this.id)">
<input type="button" id="save3I'm a photographer" class="saveBtn" 
    name="I'm a photographer" value="Save" style="visibility:hidden" onclick="save3(this.name);">

Open in new window


you have two inputs here, BUT they are NOT used as Inputs, I believe if you use regular NON input BUTTON without a NAME attribute, it would be better

<div class="someRow">
  <div id="about<?php echo $num; ?>"><?php echo $aboutme; ?> </div>
  <button onclick="edit3(this)">EDIT</button>
  <button onclick="save3(this)">SAVE</button>
</div>

Open in new window


In the arrangement above there is Only ONE ID is there, and PHP only places the DB text in the title <div> like "I'm an Actor"
Now in the JS function edit3( ), it gets the button clicked in the parameter, and then Instead of doing all of that ID code, the JS will find the parent element
       .parentElement
which is the <div class="someRow"> in JS , , , and then get the child elements by the tag
       .getElementsByTagName( )
or by the child element the title div by order number
       .children[0]

here is some code I did in firefox, which seems to do what you want for the change text to editable <input> and then save the NEW input value to server, but I do not DO ANY AJAX, just the javascript
<script>

function edit3(but) {
// get parent and then first child <div>
  var div0 = but.parentElement.getElementsByTagName("div")[0];
  var ih = div0.innerHTML; //record the text of div
  div0.innerHTML = "<input type='text' />";// insert an input
  div0.firstElementChild.value = ih;// set input value

// now get buttons and change visibility
  var buts = but.parentElement.getElementsByTagName("button");
  buts[0].style.visibility = "hidden";// edit button
  buts[1].style.visibility = "visible";
    
}

function save3(but) {
// get parent and then first child <div>
  var div0 = but.parentElement.getElementsByTagName("div")[0];
  var input0 = div0.children[0];// get input as firt clild
  alert("ID is= "+div0.id+", Input Value is= "+input0.value);// see what you got
  // update_value3(div0.id, input0.value); // send id and new text to ajax function

// now Restore back to normal mode
  div0.innerHTML = input0.value;
  var buts = but.parentElement.getElementsByTagName("button");
  buts[0].style.visibility = "visible"; 
  buts[1].style.visibility = "hidden";  
}

</script>

<style>
.someRow {
width:12em;
padding:7px;
background: #fdd;
margin: 4px;
}
</style>

<div class="someRow">
  <div id="about1">I'm the "Joker" </div>
  <button onclick="edit3(this)">EDIT</button>
  <button onclick="save3(this)" style="visibility:hidden">SAVE</button>
</div>
<div class="someRow">
  <div id="about2">I do Programming </div>
  <button onclick="edit3(this)">EDIT</button>
  <button onclick="save3(this)" style="visibility:hidden">SAVE</button>
</div>

Open in new window


this works in firefox, and I hope will give you some Ideas on a new way to try and ACCESS neighbor elements to buttons in a certain display ROW.
Thanks for this head-up Slick812. I'm almost there!
Now using your code above, I'm able to get the text inside the input field at the click of Edit button, with the edit button hidden. Now another issue came up. My Save button did not show up. Rather I got an Uncaught TypeError: Cannot read property 'style' of undefined on this line:
buts[1].style.visibility = "visible";

Open in new window


By the way, I wrap my div and button in a table like this:

<table>
<tr>
 <td><div id="about<?php echo $num; ?>"><?php echo $aboutme; ?> </div></td>
  <td><button onclick="edit3(this)">EDIT</button></td>
  <td><button onclick="save3(this)">SAVE</button></td>
</tr>
</table>

Open in new window


And my JS is:

function edit(but) {
    // get parent and then first child <div>
    var div0 = but.parentNode.previousSibling.getElementsByTagName("div")[0];
    var ih = div0.innerHTML; //record the text of div
    div0.innerHTML = "<input type='text' />"; // insert an input
    div0.firstElementChild.value = ih; // set input value

    // now get buttons and change visibility
    var buts = but.parentElement.getElementsByTagName("button");
    buts[0].style.visibility = "hidden"; // edit button
    buts[1].style.visibility = "visible";
}

Open in new window


Any reason why the save button is been seeing as undefined?
ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial