How to modify a dynamically created content using jQuery - javascript

Firstly a bit of background: I'm modifying Drupal's backend (Node creation form) to dynamically add an html to a dynamically created element.
So, this is not a strictly Drupal question, as I believe what I want to know is within the realm of jQuery rather than Drupal.
In my form, I have a repeater (initially I have 1 textarea element, when I click on 'Add more' I'll have 2 textarea elements, and so on).
What I'd like to achieve is, trigger an event (hide elements, add others) when the 'Add more' button is clicked.
So I wrote this:
(function($) {
'use strict';
$(document).ready(function() {
var $container = $('#edit-field-updates tbody .draggable td div.form-item');
$container.find('textarea').hide();
var select = '<select><option value="template_1">Template One</option><option value="template_2">Template Two</option><option value="custom">Custom</option></select>';
$container.append(select);
$('html').click(function (event) {
$container.find('textarea').hide();
$container.append(select);
});
});
})(jQuery);
Note that the I achieve what I want on page load, which is to modify the element. The problem appears when the user click on 'Add more' to add more items.
PS: Ideally I'd like to post the original code that generates the repeater however I still haven't found it yet. This admin theme is based on the Rubik theme, but its a child theme developed internally by someone who's left so can't figure it out where it is.
I've also tried:
...
$('html').on('click', 'input', function (event) {
alert('OI');
$container.find('textarea').hide();
$container.append(select);
});
...
Which does trigger the alert when I click on the page for the second time (I click on 'Add more', then click again anywhere on the page. I guess because I used 'html' rather than a element), however when I used a specific element rather than 'html' it didn't work.
Any ideas?
EDIT:
I've also tried:
$('#edit-field-updates-und-add-more').click(function() {
$container.find('textarea').hide();
$container.append(select);
});
Which didn't work. Here's the html:
<div class="field-type-text-long field-name-field-updates field-widget-text-textarea form-wrapper" id="edit-field-updates"><div id="field-updates-add-more-wrapper"><div class="form-item"><table id="field-updates-values" class="field-multiple-table sticky-enabled">
<thead><tr><th colspan="2" class="field-label"><label>Updates </label></th><th>Order</th> </tr></thead>
<tbody>
<tr class="draggable odd"><td class="field-multiple-drag"></td><td><div class="form-item form-type-textarea form-item-field-updates-und-0-value">
<div class="form-textarea-wrapper resizable"><textarea class="text-full form-textarea" name="field_updates[und][0][value]" id="edit-field-updates-und-0-value" cols="60" rows="5"></textarea></div>
</div>
</td><td class="delta-order"><div class="form-item form-type-select form-item-field-updates-und-0--weight">
<label class="element-invisible" for="edit-field-updates-und-0-weight">Weight for row 1 </label>
<select class="field_updates-delta-order form-select" id="edit-field-updates-und-0-weight" name="field_updates[und][0][_weight]"><option value="0" selected="selected">0</option></select>
</div>
</td> </tr>
</tbody>
</table>
<div class="clearfix"><input class="field-add-more-submit button-add form-submit" type="submit" id="edit-field-updates-und-add-more" name="field_updates_add_more" value="Add another item"></div></div></div></div>

Use...
$("#AddMoreButton").click(function() {
Your Code Here
});
To add the click event only onto the button rather then the whole page. You can wrap that button .click() function with a document.ready() function so that the click event is set on the button when the page loads.
$(document).ready(function() {
$("#AddMoreButton").click(function() {
Your Code Here
});
});
When you set...
$('html').on('click', 'input', function (event) {...
It causes the entire HTML doc to have the click event set on it.
Things to note: #AddMoreButton corresponds to the ID set on the button so it would look like this.
<button id="AddMoreButton">Add More</button>

Related

Activating td button using JavaScript

I have the following function, which is used to expand and collapse child tr rows from parent rows within a table, and also changes the text style of a tr to normal:
$( document ).ready(function() {
$('.parent').on('click', function(){
$(this).next('.child').toggle();
$(this).css('font-weight', 'normal');
<<< ADD COMMAND TO TOGGLE BUTTON HERE >>>
});
});
I also have the following hidden button within each tr, which I want to submit when a tr is clicked:
<button type="submit" name="read-button" formmethod="POST" value="{{ message.message_id }}" style="display: none;"></button>
Which command should I include alongside the JavaScript function, in order to achieve this? I believe it will be one of the following (provided by this answer), however I've only been using JS for a few days so I'm not sure how to include these in my code:
document.getElementById('read-button').submit();
changeAction('read-button','loginForm');
document.forms['read-button'].submit();
Sample html:
<form method=['POST']>
<table>
<tr class="parent">
<td>
<button type="submit" name="read-button" formmethod="POST" value="{{ message.message_id }}" style="display: none;"></button>
<a>Heres a cell</a>
</td>
<td>
<a>
Heres one more cell
<a>
</td>
</tr>
<tr class="child">
<td>
<a>
Some hidden info
</a>
</td>
<td>
<a>
More hidden info
</a>
</td>
</tr>
<table>
</form>
To answer the specific question of how to trigger a button within a td via clicking on the tr:
$('.parent').on('click', function() {
$(this).find("button").click();
that can be improved by giving the button a class (incase you add additional buttons in future), eg:
<button class='readbutton' ..`
then
$('.parent').on('click', function() {
$(this).find("button.readbutton").click();
In this scenario, it seems that you don't need a hidden button as you can call the functionality of the hidden button directly.
So rather than:
$("button").click(handleClick);
use
$('.parent').on('click', function() {
.. other functionality ...
handleClick();
as you're new to js, note the difference between handleClick and handleClick() - with () it calls the function, without it passes it as a function variable.
$("button").click(handleClick);
is the same as
$("button").click(function() { handleClick(); });
If you're trying to submit a form, then you would use $(form).submit() - but there's no form in the code as pointed out in the comments and an ajax call would seem more appropriate here than submitting a form.
Finally, consider using classes rather than setting the css directly as adding/removing classes it quite simple in jquery eg for your tr click event add:
$(this).addClass("selected").siblings().removeClass("selected");

Add listener to dynamically added HTML within closure?

I have a list of locations in google maps that a user can click. When they click, I generate a template that displays the information for that place. Right now, I have a container div that I call .replaceWith() on with the newly generated template.
When a user tries to click a button in that template, I want a place-specific action to take place (e.g. pin the place). To do this, I need the place ID saved somewhere so that the listener code knows what to do. I was hoping to use a closure to create the listener on the fly so that I could "enclose" the ID of the place that the viewer is getting details for:
function selectPlace(place_id) {
// Swap out the old template
$("#listing-placeholder").replaceWith(listing_info_template(place));
// Create a listener to handle clicks while remembering the place_id
(function(){
$('.save_button').click(function(){
alert("Clicked handler for " + place_id);
});
})();
}
This doesn't seem to work at all; my listener never fires. It seems like this is related to the fact that .save_button is inside of the template (dynamically added). Not sure why this is.
I am hesitant to use .on() because then I would have to put the ID somewhere in the template, which feels really hacky to me. I know that backbonejs somehow binds events to dynamically inserted templates so that any relevant context is still available to events -- that's effectively what I'm looking to mimic here.
So, any suggestions on how I can add a listener to a dynamically created element in such a way that the listener receives key information about the "object" that element is conceptually representing, without bloating the HTML with extra metadata (I've seen people make the id's like "save_button-" and then split on the dash, but that seems extremely hacky?)?
Thanks!
I've used a variable with a broad scope to hold selected Id in the quick and dirty example below.
var selectedId; //Holds seleced ID
function selectPlace(place_id) {
// Swap out the old template
//$("#listing-placeholder").replaceWith(listing_info_template(place));
$("#listing-placeholder").replaceWith($("#section" + place_id));
//Sotre the ID here
selectedId = place_id;
}
$(document).ready(function() {
$("#cbId").change(function() {
selectPlace($(this).val())
});
//Set up event listener. Change #target as appropriate for you
$("#target").on("click", ".save_button", function() {
alert(selectedId);
});
});
#sections {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>
<label>Select a section
<select id="cbId">
<option></option>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
</label>
</div>
<div id="target">
<div id="listing-placeholder">
I'm the place holder
</div>
</div>
<div id="sections">
<!--No Event Listeners Will be bound directly untill template is replaced -->
<div id="section1">
<h2>I'm section 1</h2>
<input type="button" value="Save Button" class="save_button">
</div>
<div id="section2">
<h2>I'm section 2</h2>
<input type="button" value="Save Button" class="save_button">
</div>
<div id="section3">
<h2>I'm section 3</h2>
<input type="button" value="Save Button" class="save_button">
</div>
</div>
I will just try to correct your actual approach by passing the parameter to your closure, but you may find a better approach.
In your case you need to pass the place_id as a parameter to the closure too, this is how should be your code:
function selectPlace(place_id) {
// Swap out the old template
$("#listing-placeholder").replaceWith(listing_info_template(place));
// Create a listener to handle clicks while remembering the place_id
(function(id){
$('.save_button').click(function(){
alert("Clicked handler for " + id);
});
})(place_id);
}
I changed the variable name so you can get the difference between the two parameters.
You actually have to use .on, it's the way jQuery matches all dynamically added elements. But you don't need to do anything "hacky", you just have to bind the listener to a parent element
`$(parent).on('click', '.save_button', {});
function selectPlace(place_id) {
// Swap out the old template
$("#listing-placeholder").replaceWith(listing_info_template(place));
// Create a listener to handle clicks while remembering the place_id
$(document).on('click', '.save_button').click(function(){
alert("Clicked handler for " + place_id);
});
}

Mark row for deleting using checkbox

I have this inbox or mails which I needed to mark using checkbox for multiple deletions. The problem is that when I click the checkbox it always go to method read() because of onclick inside the tag. All I want is simple marking. Please see the code below:
#foreach ($messages as $message)
<tr onclick="document.location='{{route('account.message.read', array($message->id))}}'">
<td> <input type="checkbox" name="msg" value="{{$message->id}}"></td>
<td><strong>{{Str::words($message->subject, 5, '...')}}</strong> {{Str::words($message->message, 20, '...')}}</td>
</tr>
#endforeach
Now how can I mark each row without going inside that method? I am a newbie btw.
All you need to is attach handler to checkbox and use stopPropagation from bubbling up into trees, see below example :
HTML
Supposed we have table rows with onclick inline javascript and inside it have input:
<table>
<tr onclick="return alert('Hai')">
<td>
<input type="checkbox" />Click Me
</td>
</tr>
<table>
And here the handler :
$('input').click(function(e){
// normally if we removed this line of code
// alert will getting called as checkbox is checked/unchecked
// was inside table rows
// but with this propagation, we disabled it
// from bubbling up into tree
e.stopPropagation();
});
DEMO
you could put the onclick inside the tags excluding the checkbox cell
otherwise you could call an actual onclick function while capturing the click event and check that x is greater than lets say 50px!
<script type="text/javascript">
document.getElementById('emailRow1').addEventListener("click",function(e){
var x=e.pageX-this.offsetLeft;
if(x>50){
rowClicked();
}
},true);
</script>
<table><tbody><tr id='emailRow1'><td style="width:50px;">This area wont trigger onclick</td><td>I trigger onclick</td><td>i trigger onclick</td>
Then you can set custom parameters on your object and recall them using this.customPropertyName to determine what row was clicked! hope this helps

How can I add "tags" to a search field using Jquery

I have some tags (tagbutton) in a table, each tag has its own id, what I want to achieve is when the user clicks on the tag, a hidden input is created in the form with the value of the div (or tag) that has been clicked on. I also want the clicked div to be copied in the tagselected div.
I have no idea how to do that on jquery. Thank you very much in advance for your help.
<table> <tr>
<td> <div class="tagbutton" id="jazz"> Jazz </div> </td>
<td> <div class="tagbutton" id="classical"> Classical </div> </td>
<td> <div class="tagbutton" id="R&B"> R&B </div> </td>
</tr> </table>
<div id="tagselected"> </div>
<form> <input type="text"> <button ="submit"> Submit </button> </form>
Here is the javascript function that I have to copy the div, however when I clicked on it the entire table is copied
$('#jazz').click(function () {
$('.tagbutton').clone().insertAfter("#tagselected");
});
This code is wrong:
$('#jazz').click(function () {
$('.tagbutton').clone().insertAfter("#tagselected");
});
The problem with this code is that you are retrieving all the items with class tagbutton on the whole page. If your click function is on the item you want then you should be able to just use this to access the clicked item.
so something like :
$(this).clone().insertAfter("#tagselected");
This code is not tested and is just the simple change of the initial jQuery selector.
I assume the problem you have with the hidden fields is the same - that you were selcting all tags instead of just the one you clicked so hopefully this will solve that problem too.

Using .change() function to auto-submit form on checkbox change?

This is a bit of a long question so please bear with me guys.
I needed to make a form submit automatically when a checkbox was ticked. So far I have the code below and it works perfectly. The form must submit when the check box is either checked or unchecked. There is some PHP that reads a database entry and shows the appropriate status (checked or unchecked) on load.
<form method="post" id="edituser" class="user-forms" action="--some php here--">
<input class="lesson" value="l101" name="flesson" type="checkbox" />
</form>
<script>
$('.lesson').change(function() {
$('.user-forms').submit();
});
</script>
However, when I introduce a fancy checkbox script which turns checkboxes into sliders it no longer works. The checkbox jQuery script is below:
<script src="'.get_bloginfo('stylesheet_directory').'/jquery/checkboxes.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
$("input[type=checkbox]").tzCheckbox({labels:["Enable","Disable"]});
});
</script>
The contents of the checkboxes.js called to above is as follows:
(function($){
$.fn.tzCheckbox = function(options){
// Default On / Off labels:
options = $.extend({
labels : ['ON','OFF']
},options);
return this.each(function(){
var originalCheckBox = $(this),
labels = [];
// Checking for the data-on / data-off HTML5 data attributes:
if(originalCheckBox.data('on')){
labels[0] = originalCheckBox.data('on');
labels[1] = originalCheckBox.data('off');
}
else labels = options.labels;
// Creating the new checkbox markup:
var checkBox = $('<span>',{
className : 'tzCheckBox '+(this.checked?'checked':''),
html: '<span class="tzCBContent">'+labels[this.checked?0:1]+
'</span><span class="tzCBPart"></span>'
});
// Inserting the new checkbox, and hiding the original:
checkBox.insertAfter(originalCheckBox.hide());
checkBox.click(function(){
checkBox.toggleClass('checked');
var isChecked = checkBox.hasClass('checked');
// Synchronizing the original checkbox:
originalCheckBox.attr('checked',isChecked);
checkBox.find('.tzCBContent').html(labels[isChecked?0:1]);
});
// Listening for changes on the original and affecting the new one:
originalCheckBox.bind('change',function(){
checkBox.click();
});
});
};
})(jQuery);
There is also some CSS that accompanies this script but I am leaving it out as it is not important.
Finally, this is what the jQuery script does to the checkbox:
<input id="on_off_on" class="lesson" value="lesson11-1" name="forexadvanced[]" type="checkbox" style="display: none; ">
<span classname="tzCheckBox checked" class=""><span class="tzCBContent">Disable</span><span class="tzCBPart"></span></span>
When the checkboxes are changed into sliders the .change() function no longer detects the change in the checkboxes status.
How can I make the .change() function work or is their an alternative function I can use?
This plugin changes your checkboxes to span elements and hides the actual checkboxes themselves. Thus, when you click on them, nothing happens. Since span elements don't have onchange events, you can't bind change events to these.
However, span elements do have click events, meaning that you could instead bind a click event to the generated spans, using Firebug or Chrome Debugger to locate the correct element to bind to.
Your click-handler can then take the same action your change event would normally take if the plugin weren't being used.
Here is an example:
HTML (Source):
<!-- This is a checkbox BEFORE running the code that transforms the checkboxes
into sliders -->
<li>
<label for="pelda1">OpciĆ³ 1:</label>
<input class="pelda" type="checkbox" id="pelda1" name="pelda1" />
</li>
HTML (Generated From Chrome Debugger):
NOTE: This is the generated HTML after running the JavaScript that converts checkboxes to sliders! You must bind your click event AFTER this code is generated.
<li>
<label for="pelda1">Option 1:</label>
<!-- The hidden checkbox -->
<input class="pelda" type="checkbox" id="pelda1" name="pelda1" style="display: none; " />
<!-- the "checked" class on the span gets changed when you toggle the slider
if it's there, then it's checked. This is what you're users are actually
changing.
-->
<span class="tzCheckBox checked">
<span class="tzCBContent">active</span>
<span class="tzCBPart"></span>
</span>
</li>
JavaScript:
NOTE: This must be bound AFTER converting the checkboxes to sliders. If you try it before, the HTML won't yet exist in the DOM!
$('.tzCheckBox').click(function() {
// alert the value of the hidden checkbox
alert( $('#pelda1').attr("checked") );
// submit your form here
});
Listen for change like this:
$('.lesson').bind("tzCheckboxChange",function() {
$('.user-forms').submit();
});
Modify the plugin by adding the line:
$(originalCheckBox).trigger("tzCheckboxChange");
after
checkBox.find('.tzCBContent').html(labels[isChecked?0:1]);
This way, anytime you use this plugin, you can listen for tzCheckboxChange instead of just change. I don't really know what's going on with the plugin, but seems kinda funky for it to be listening for a change event when it would only be fired through trigger (unless it doesn't hide the original checkbox).

Categories

Resources