I'm trying to call a function for an html element using jquery. I'm struggling with this has been hours and I can't figure out what is wrong.
I have an answer which I want to mark as solved. A question have multiple answers.
As far as I know the function is being fired the number of answers I have in the question. If I have two answers, the function will run twice and so on.
$(document).ready(function () {
$(".accepted.ans").on('click', function (e) {
e.preventDefault();
var parent = $(this).closest('.accept');
console.log(parent);
var current = $(this);
console.log(current);
var url = parent.data('url');
var qid = parent.data('question');
var aid = parent.data('answer');
$.get(url + '?question=' + qid + '&answer=' + aid, function (data) {
console.log("Reading...");
data = $.parseJSON(data);
console.log(aid);
console.log(e);
setAcceptedStatus(current, data.result);
});
});
});
function setAcceptedStatus(object, status) {
if (status === true) {
object.addClass('active');
}
}
This is my jQuery function. The one that I want to be called exactly once for each answer when I press the accept ans div element, which is:
<blockquote class="accept-answer text-right {if !$isMine} hidden{/if}" >
<div class="accept"
title="Accept this answer"
data-url="{url('controller/api/questions/mark_as_solved')}"
data-refresh="{url('controller/api/questions/refresh_accepted_answers')}"
data-answer="{$answer['answerid']}"
data-question="{$question['publicationid']}">
<div class="accepted ans"
id="{$answer['answerid']}"
title="Unnacept this answer">
</div>
</div>
</blockquote>
I thought this was happening because everytime I want to call that function , since I have $(".accepted.ans") it will apply to all the accepted ans it can find on the document. So I thought in adding an id to the class, something like:
id="{$answer['answerid']}"
but as I read somewhere here, it is supposed to work without that.
I really don't know why it triggers more than once, I did all kind of debug, checked the html structure and everything seems flawless.
Any kind soul got an idea in what is wrong?
Regards
I suspect that somehow the $(document).ready() function is executed as many times as your answers, so the click event handler is binded the same number of times for each answer. Perhaps it's the template that does that?
If that's indeed the problem and you can't find a solution for the template, try changing
$(".accepted.ans").on('click', function (e) {
to
$(".accepted.ans").off('click').on('click', function (e) {
to unbind all click event handlers but the last one.
try changing .closest(... to .parent(). Snippet below...
$(document).ready(function() {
$(".accepted.ans").on('click', function(e) {
e.preventDefault();
var current = $(this),
parent = current.parent();
var url = parent.data('url');
var qid = parent.data('question');
var aid = parent.data('answer');
console.log(aid);
$.get(url + '?question=' + qid + '&answer=' + aid, function(data) {
console.log("Reading...");
data = $.parseJSON(data);
console.log(aid);
console.log(e);
setAcceptedStatus(current, data.result);
});
});
});
function setAcceptedStatus(object, status) {
if (status === true) {
object.addClass('active');
}
}
.ans {
position: static;
width: 90%;
height: 5em;
background: #f90;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<blockquote class="accept-answer text-right {if !$isMine} hidden{/if}">
<div class="accept" title="Accept this answer" data-url="{url('controller/api/questions/mark_as_solved')}" data-refresh="{url('controller/api/questions/refresh_accepted_answers')}" data-answer="one" data-question="{$question['publicationid']}">
<div class="accepted ans" id="{$answer['answerid']}" title="Unnacept this answer">
</div>
</div>
</blockquote>
<blockquote class="accept-answer text-right {if !$isMine} hidden{/if}">
<div class="accept" title="Accept this answer" data-url="{url('controller/api/questions/mark_as_solved')}" data-refresh="{url('controller/api/questions/refresh_accepted_answers')}" data-answer="two" data-question="{$question['publicationid']}">
<div class="accepted ans" id="{$answer['answerid']}" title="Unnacept this answer">
</div>
</div>
</blockquote>
<blockquote class="accept-answer text-right {if !$isMine} hidden{/if}">
<div class="accept" title="Accept this answer" data-url="{url('controller/api/questions/mark_as_solved')}" data-refresh="{url('controller/api/questions/refresh_accepted_answers')}" data-answer="three" data-question="{$question['publicationid']}">
<div class="accepted ans" id="{$answer['answerid']}" title="Unnacept this answer">
</div>
</div>
</blockquote>
Using jQuery, you need to prevent it's function listened by other event attached to its parental tags. So try the stopPropagation(); instead.
$(document).ready(function () {
$(".accepted.ans").on('click', function (e) {
e.stopPropagation();
e.preventDefault();
var parent = $(this).closest('.accept');
console.log(parent);
var current = $(this);
console.log(current);
var url = parent.data('url');
var qid = parent.data('question');
var aid = parent.data('answer');
$.get(url + '?question=' + qid + '&answer=' + aid, function (data) {
console.log("Reading...");
data = $.parseJSON(data);
console.log(aid);
console.log(e);
setAcceptedStatus(current, data.result);
});
});
});
function setAcceptedStatus(object, status) {
if (status === true) {
object.addClass('active');
}
}
Related
I have tried javascript and JQuery. I know how to write the code to get the cell values from my first tab but the same function does not work on the other tabs on my webpage. It seems as if the table in my other tabs is just a view. I am new to javascript and JQuery so think I might be missing something easy. I have used ".on" in my click function and that doesn't help. Here is the Javascript code and JQuery code, both work by grabbing the cell value I click but only for the first tab:
JavaScript
init();
function init(){
addRowHandlers('customerTable');
}
function addRowHandlers(tableId) {
if(document.getElementById(tableId)!=null){
var table = document.getElementById(tableId);
var rows = table.getElementsByTagName('tr');
var cid = '';
var name = '';
for ( var i = 1; i < rows.length; i++) {
rows[i].i = i;
rows[i].onclick = function() {
cid = table.rows[this.i].cells[0].innerHTML;
name = table.rows[this.i].cells[1].innerHTML;
alert('cid: '+cid+' name: '+name);
};
}
}
}
JQuery
$('#customerTable').find('tr').click(function() {
var $id = $(this).closest("tr")
.find(".custId")
.text();
var $name = $(this).closest("tr")
.find(".custName")
.text();
alert($name);
$('.defaultTextBox.text_custId:text').val($id);
$('.defaultTextBox.text_custName:text').val($name);
});
In the end my goal is to get the elements clicked and set the text in my text boxes to those values, which you can see I did in the JQuery, but it only works on my first page. I need the click in my table to work on all tabs. Thanks in advance!
Edit
<div id="removeCustomer" class="tabcontent">
<h3>Pick a customer to remove!</h3>
<div class="container">
<br />
<h2 align="center">Search here to find the customer you want to remove</h2><br />
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">Search</span>
<input type="text" name="search_text" id="search_text" placeholder="Search by name, phone number, email, or state" class="form-control" />
</div>
</div>
<br />
<div class="result"></div>
</div>
</div>
The "removeCustomer" id is one of the tabs. So I have multiple tabs using the same, "result", which I think is the problem I just do not know how to solve it. If I Left out "result" it would not generate a table.
Here is the JQuery which uses a php file to connect to my database and get my data. And this is what generates result.
JQuery
$(document).ready(function(){
load_data();
function load_data(query)
{
$.ajax({
url:"fetchCustomers.php",
method:"POST",
data:{query:query},
success:function(data)
{
$('div.result').html(data);
}
});
}
$('input.form-control').keyup(function(){
var search = $(this).val();
if(search != '')
{
load_data(search);
}
else
{
load_data();
}
});
});
Thanks again.
Every time a selection is made from a dropdown menu, specific data is pulled from facebook and added to different divs. I am trying to update the contents of the div every time a different selection is made, however at the minute, the contents are just appended on after the initial contents.
This is the code that gets data based on a selection and creates the list from the returned data
<script>
city = document.getElementById("citySelection")
city.addEventListener("change", function() {
var selected = this.value;
var eventsList = document.getElementById("events");
if (selected == "None") {
eventsList.style.display = "none";
} else {
eventsList.style.display = "block";
};
if (selected == 'Bristol') {
getBristolEvents();
};
if (selected == 'Leeds') {
getLeedsEvents();
};
if (selected == 'Manchester') {
getManchesterEvents();
};
if (selected == 'Newcastle') {
getNewcastleEvents();
};
});
function createList(response, listId) {
var list = document.createElement('UL')
for (var i = 0; i < 10; i++) {
var events = response.data[i].name
var node = document.createElement('LI');
var textNode = document.createTextNode(events);
node.appendChild(textNode);
list.appendChild(node)
listId.appendChild(list);
}};
</script
This is the div being targeted:
<html>
<div id="events" style="display: none">
<div id="eventsDiv" style="display: block">
<div id="eventsListOne">
<h3 id='headerOne'></h3>
</div>
<div id="eventsListTwo">
<h3 id='headerTwo'></h3>
</div>
<div id="eventsListThree">
<h3 id='headerThree'></h3>
</div>
</div>
</div>
</div>
</html>
I have tried resetting the innerHtml of the div every time the function to get the data from facebook is called:
<script>
function getEventsThree(fbUrl, title) {
var listId = document.getElementById('eventsListThree');
var headerThree = document.getElementById('headerThree');
listId.innerHtml = "";
headerThree.append(title)
FB.api(
fbUrl,
'GET', {
access_token
},
function(response) {
listId.innerHtml = createList(response, listId)
}
)};
</script>
However, that still doesn't reset the contents of the div.
I've looked at other response but they all use jquery which I am not using.
Can anyone advise on the best way to fix this? Thanks.
I think your Hennessy approach is fine. Generate the inner content, then set .innerHTML.
At least one of your problems, maybe the only one, appears to be that you set .innerHTML to the return value of createList, but that function does not return anything.
I'm trying to create an upload form. It's working well so far and i'm trying to sort out a couple of bugs that I dislike.
The line that I seem to be having trouble with is
$(element).find(">:first-child").attr("value", "");
When cloning the form, it clones the div and replaces the value with nothing leaving a blank form, this works well, if I were to delete that line I would get the previous form's value, so it would be nice for a blank form to show.
The issue i'm having is when you delete a form all the forms values delete, What I want is when you delete a form, leave the value alone for the other forms.
Here's a fiddle http://jsfiddle.net/d77pd/1/ or see code below
HTML
<button class="clone">Add an Image</button>
<div id="upload_image_sets">
<div id="clonedInput1" class="clonedInput">
<input type="text" id="upload_image_link_1" class="image" size="36" name="hero_options[upload_image_link_1]" value="' . $hero_options['upload_image_link_1'] . '" />
<input id="show_upload_image_link_button_1" class="button upload_images" type="button" value="Upload Image" />
<button class="remove">Remove</button>
</div>
</div>
JavaScript
function updateClonedInput(index, element) {
$(element).appendTo("#upload_image_sets").attr("id", "clonedInput" + index);
$(element).find(">:first-child").attr("id", "cs_product_menu_img_src_" + index);
$(element).find(">:first-child").attr("name", "hero_options[upload_image_link_" + index + "]");
$(element).find(">:first-child").attr("value", "");
$(element).find(">:first-child").next().attr("id", "cs_product_menu_img_src_" + index + "_button");
displayRemove();
}
function displayRemove() {
if ($('.clonedInput').length === 1) {
$('.remove').hide();
} else {
$('.remove').show();
}
}
displayRemove();
$(document).on("click", ".clone", function (e) {
e.preventDefault();
var cloneIndex = $(".clonedInput").length + 1;
var new_Input = $(this).closest('.clonedInput').length ? $(this).closest('.clonedInput').clone() : $(".clonedInput:last").clone();
updateClonedInput(cloneIndex, new_Input);
});
$(document).on("click", ".remove", function (e) {
e.preventDefault();
$(this).parents(".clonedInput").remove();
$(".clonedInput").each(function (cloneIndex, clonedElement) {
updateClonedInput(cloneIndex + 1, clonedElement);
})
});
Clone the form a few times, if you delete any form apart form the 1st one with the content, you'll notice the first form's content deletes, I want this left alone.
First approach:
call $(element).find(">:first-child").attr("value", ""); after calling updateClonedInput(cloneIndex, new_Input); from add function.
Working Demo First approach:
Second Approach:
I have modified some code. pass one more bool argument in function updateClonedInput.which will be set true when added and set false when dom is removed.This will prevent the value getting replaced on remove function:
function updateClonedInput(index, element,param) {
$(element).appendTo("#upload_image_sets").attr("id", "clonedInput" + index);
$(element).find(">:first-child").attr("id", "cs_product_menu_img_src_" + index);
$(element).find(">:first-child").attr("name", "hero_options[upload_image_link_" + index + "]");
if(param)
$(element).find(">:first-child").attr("value", "");
$(element).find(">:first-child").next().attr("id", "cs_product_menu_img_src_" + index + "_button");
displayRemove();
}
function displayRemove() {
if($('.clonedInput').length === 1) {
$('.remove').hide();
} else {
$('.remove').show();
}
}
displayRemove();
$(document).on("click", ".clone", function(e){
e.preventDefault();
var cloneIndex = $(".clonedInput").length + 1;
var new_Input = $(this).closest('.clonedInput').length ? $(this).closest('.clonedInput').clone() : $(".clonedInput:last").clone();
updateClonedInput(cloneIndex, new_Input,true);
});
$(document).on("click", ".remove", function(e){
e.preventDefault();
$(this).parents(".clonedInput").remove();
$(".clonedInput").each( function (cloneIndex, clonedElement) {
updateClonedInput(cloneIndex + 1, clonedElement,false);
})
});
Working Demo Second Approach
An alternate solution that creates a blank clone from the first element once, then uses this every time a new row is required. It also uses CSS to hide/show the Remove button based on the fact that you only need Remove buttons on all rows unless it's the only child.
Disclaimer: I have removed the id manipulation as I am unsure if you really need it. I can update if necessary.
Demo
HTML
<button class="clone">Add an Image</button>
<div id="upload_image_sets">
<div class="clonedInput">
<input type="text" class="image" size="36" name="hero_options[upload_image_link_1]" value="an initial value" />
<input class="button upload_images" type="button" value="Upload Image" />
<button class="remove">Remove</button>
</div>
</div>
CSS
.clonedInput .remove {
display:inline-block;
}
.clonedInput:only-child .remove {
display:none;
}
JavaScript
function resetForm($form) {
$form.find('input:text, input:password, input:file, select, textarea').val('');
$form.find('input:radio, input:checkbox').removeAttr('checked').removeAttr('selected');
}
var $blankClone = $('.clonedInput').clone();
resetForm($blankClone);
$(document).on('click', '.clone', function(e) {
e.preventDefault();
$blankClone.clone().appendTo('#upload_image_sets');
});
$('#upload_image_sets').on('click', '.remove', function(e) {
e.preventDefault();
$(this).closest('.clonedInput').remove();
});
resetForm() borrowed from Resetting a multi-stage form with jQuery
Extending the example found at Autosave in MVC (ASP.NET), I wanted to create a partial to reuse in my application. I have one view with a tabbed layout, and each tab has its own form, and this is causing problems, namely that every form tries to submit every time, and only the first timestamp in the document updates. I understand why this is happening, but I don't know how I can fix it.
Partial's cshtml:
<div class="form-group">
<label class="control-label col-lg-2" for=""> </label>
<div class="col-lg-10">
<span class="help-block" id="autosaveTime">Not Autosaved</span>
</div>
</div>
#{
var autosaveString = "'" + #ViewData["autosaveController"] + "'";
if (ViewData["autosaveAction"] != null && ViewData["autosaveAction"] != "")
autosaveString += ", '" + ViewData["autosaveAction"] + "'";
}
<script type="text/javascript">
$(document).ready(function () {
autosave(#Html.Raw(autosaveString));
});
</script>
Javascript:
//methodName is optional-- will default to 'autosave'
function autosave(controllerName, methodName)
{
methodName = typeof methodName !== 'undefined' ? methodName : 'autosave'
var dirty = false;
$('input, textarea, select').keypress(function () {
dirty = true;
});
$('input, textarea, select').change(function () {
dirty = true;
});
window.setInterval(function () {
if (dirty == true) {
var form = $('form');
var data = form.serialize();
$.post('/' + controllerName + '/' + methodName, data, function () {
$('#autosaveTime').text("Autosaved at " + new Date);
})
.fail(function () {
$('#autosaveTime').text("There was a problem autosaving, check your internet connection and login status.");
});
dirty = false;
}
}, 30000); // 30 seconds
}
I have 2 ideas on how to fix it, but not sure which is more maintainable/workable:
Give each form an id, and pass that to the partial/autosave function. Add the name to the autosavetime text block for updates, and to determine which form to serialize/submit.
Somehow use jquery's closest function to find the form where the autosave block was placed, and use that to do what I was doing explicitly with #1.
First, make the URL using your Razor helper's Html extension (dynamically piecing URLs like this in JavaScript is unnecessarily risky). Take that, and stuff it in a data attribute on the tab control like so:
<div class="tab autosave" data-action-url='#Html.Action("Action", "Controller")'>
<form>
<!-- Insert content here -->
</form>
</div>
Then, you'll want something like this ONCE -- do not include it everywhere, and remove the javascript from your partial completely:
$(function() {
// Execute this only once, or you'll end up with multiple handlers... not good
$('.autosave').each(function() {
var $this = $(this),
$form = $this.find('form'),
dirty = false;
// Attach event handler to the tab, NOT the elements--more efficient, and it's always properly scoped
$this.on('change', 'input select textarea', function() {
dirty = true;
});
setInterval(function() {
if(dirty) {
// If your form is unobtrusive, you might be able to do something like: $form.trigger('submit'); instead of this ajax
$.ajax({
url : $this.data('action-url'),
data : $form.serialize()
}).success(function() {
alert("I'm awesome");
dirty = false;
});
}
}, 30 * 1000);
});
});
I have list of divs with some data in there:
<div style="border: 1px solid #dddddd;">
<div id="wrap">
<h3 id="cText">#Model.CommentText</h3>
<a id="runEdit" href="#" >Edit</a>
</div>
</div>
When user click on runEdit link I make edit from this:
e.preventDefault();
var txt = $('#cText').text();
$('#cText').remove();
$('#wrap').prepend('<textarea>' + txt + '</textarea>');
$('#wrap').append('<input type="submit" value="Ok" />');
$('#wrap').append('<input type="submit" value="Cancel" />');
The problem is I added here this two buttons in javascript. But I don't know how to attach some controller action to this buttons?
The problem here is that if I write 5 comments. And click on edit I get 5 edit forms.
$('#editButton').live('click', function (e) {
e.preventDefault();
var container = $(this).closest('.commentWrap');
var itemId = container.attr('id');
var nestId = '#' + itemId;
var txt = $('#commentTextValue').text();
$(nestId + ' #commentTextValue').remove();
$(nestId + ' #editButton').remove();
$(nestId).prepend('<textarea id="editArea">' + txt + '</textarea>');
$(nestId).append('<input type="submit" value="Ok" class="btnOk" />');
})
$('.btnOk').live('click', function (e) {
e.preventDefault();
var container = $(this).closest('.commentWrap');
var itemId = container.attr('id');
var text = container.find('textarea').val();
var nestId = '#' + itemId;
//alert(nestId);
$.ajax({
url: '/Comment/SaveComment',
data: JSON.stringify({ CommentText: text, CommentId: itemId }),
type: 'post',
contentType: 'application/json',
success: function (data) {
if (data.success === true) {
//alert(data.message); // do show/hide stuff here instead of the alert
$(nestId + ' #editArea').remove();
$(nestId + ' .btnOk').remove();
$(nestId).append('<h3 id="commentTextValue">' + data.message + '</h3>');
$(nestId).append('<a id="editButton" href="#">Edit</a>');
}
}
});
});
</script>
<div style="border: 1px solid #dddddd;">
#Html.ActionLink(#Model.Author, "SomeAction")
<div class="commentWrap" id="#Model.CommentId">
<p id="commentTextValue">#Model.CommentText</p>
<a id="editButton" href="#">Edit</a>
</div>
</div>
First add an itemid to the div like this, and convert the id=wrap to a class, as there are more than one of them.
<div class="wrap" id="123"></div>
That way you get a way to reference the id of the item that you are editing.
You should also add a class to the submit button that you inject on the page, fx:
<input type="submit" class="btnOk" value="Ok"/>
Then you can hook up the javascript:
$('.btnOk').live('click',function(e){
e.preventDefault();
var container = $(this).closest('.wrap');
var itemId = container.attr('id');
var text = container.find('textarea')[0].val();
$.ajax({
url: '/mycontroller/savecomment',
data: JSON.stringify({comment: text, id:itemId}), // using JSON2, see below
type: 'post',
contentType: 'application/json',
success: function(data){
if(data.success===true){
alert(data.message); // do show/hide stuff here instead of the alert
}
}
});
});
NOTE: Download the json2 library and add it to you script references - it's a good way to do your json serialization. (https://github.com/douglascrockford/JSON-js)
In your controller you must add an action method to handle the request:
public ActionResult SaveComment(string text, int id)
{
//save your comment for the thing with that id
var result = new {success = true, message = "saved ok"};
return Json(result, JsonRequestBehavior.AllowGet);
}
The answer of Marc is collrect. Surround your code with this. However I strongly recommend you to make as much "html in html" rather than in JavaScript.
The above code could be translated to a better shape, like this,
<div style="border: 1px solid #dddddd;">
<div id="wrap">
<h3 id="cText">#Model.CommentText</h3>
<a id="runEdit" href="#" >Edit</a>
</div>
<div id="editPanel" style="display:none;">
<form action="#Url("Edit", "Whatevercontroller")">
<textarea name="CommentText">#CommentText</textarea>
<input type="submit" value="Ok" />
Cancel
</form>
</div>
</div>
and js would be
function StartEdit() {
$("#wrap").css("display", "none");
$("#editPanel").css("display", "block");
}
function CancelEdit() {
$("#wrap").css("display", "block");
$("#editPanel").css("display", "none");
}
the advantage of this approach that you do not generate too much DOM elements in this case. Otherwise chances tha your JavaScript will become absolutely unmanageable.
You have to put a form tag around your textarea and to set the action of the form by the #Url.Action helper to the needed action.
You need to make Ajax calls to your controller action. Please refer to below link :
http://tugberkugurlu.com/archive/working-with-jquery-ajax-api-on-asp-net-mvc-3-0-power-of-json-jquery-and-asp-net-mvc-partial-views
You will find a sample there.
Basically, what you need to do is as follows:
var d = "poo=1&bar=2";
$.ajax({
type: "POST",
url: "/home/myaction",
data: d,
success: function (r) {
alert(r.data);
},
complete: function () {
alert("I am done!");
},
error: function (req, status, error) {
//do what you need to do here if an error occurs
alert("error");
}
});