Building a catch-all function for rendering - javascript

I am currently trying to render a calendar that uses checkboxes as filtering. I have all the filters working if I do it this way:
//Call my render event on click of a checkbox
$('.filterable-content__criteria').on('change', function(){
$('#calendar').fullCalendar('rerenderEvents');
});
This works so everytime I click a checkbox, it re-renders my calendar - thus showing/hiding events. Right now I am doing this relatively staticly where in my render function I do this:
var eventAcceptedClasses = [];
if ($('input#Event-Type-2').is(':checked')){
eventAcceptedClasses.push(id);
displayEvent = false;
} else if ($('input#Event-Type-2').is(':checked')){
eventAcceptedClasses.push('Event-Type-2');
displayEvent = false;
} else if ($('input#Event-Type-3').is(':checked')){
eventAcceptedClasses.push('Event-Type-3');
displayEvent = false;
} else {
//If none checked, show all.
eventAcceptedClasses.push('fc-event');
displayEvent = true;
};
This function is regrettably, not scalable. I'm looking to make it so on render, it takes the id of the checkbox that was clicked, then plugs it into the function ie: You set the id in the click event like this
var cb_click = event.target.id
Then my render just looks like this:
var eventAcceptedClasses = [];
if ($('input#'+cb_click ).is(':checked')){
eventAcceptedClasses.push(cb_click);
displayEvent = false;
}
But given the way the functions are nested, this doesn't seem to work due to the structure of the code.
Any ideas on how I can do this? I included a JS Fiddle that shows the full structure for clarification.
Edit:
Basic Input Structure
<input class="filterable-content__criteria" type="checkbox" name="categories" value="Event Type 1" id="Event-Type-1">

If you can modify the html, adding an onchange attribute to your input is the way to go.
It is much simpler to understand, and is better for memory usage than an event listener, or a redundant testing if else.
In a short example, with just javascript, to call a function that pass the id of the current clicked element.
//Call my render event on click of a checkbox
function call_render(me){
console.log(me)
/* eventType1 */
// me.fullCalendar('rerenderEvents');
}
Tick: <input onchange="call_render(this.id)" id="eventType1" class="filterable-content__criteria" type="checkbox" name="categories" value="Event Type 1">
Sometimes we just can't modify the html from the source.
The html can also become a bit unreadable when too much attributes are set.
It is also not so good for fixing stuffs and readability when javascript is written inside html attribute, like in the previous example.
Attributes needs to be set at the DOM level. It is better practice. There is alternatives, but it's always better to use html capabilities first.
// DOM now ready
// Set the onchange attribute to the element *eventType1*.
// And give this attribute a function when a change happen
eventType1.setAttribute("onchange","call_render(this.id)")
//Call my render event on click of a checkbox
function call_render(me){
console.log(me)
/* eventType1 */
// me.fullCalendar('rerenderEvents');
}
Tick: <input class="filterable-content__criteria" type="checkbox" name="categories" value="Event Type 1" id="eventType1">
To verify or test the checkbox state:
console.log( (eventType1.checked) )
console.log( !(eventType1.checked) )
Tick: <input id="eventType1" type="checkbox" checked="">
Again, we can also set a global event handler like this:
eventType1.oninput = (function() {
call_render(this.id)
})
Js perf test, to compare the 3 methods.
I removed the querySelector, the inline onchange is stil faster.
https://jsperf.com/global-vs-event-vs-inline

Related

JavaScript onclick return

I am new to JavaScript and I'm still figuring things out.
I already searched the web for this but I'm not quite sure what keywords should I use. I am creating some program with a random number using html and JS.
So in my javascript (inside the tag)
I have something like:
var x;
function initRandom() { // I run this function at loading time (i.e: <body onload="initRandom();">)
x = Math.random();
}
function checkGuessedNumber() { // this just checks if the number guessed by the user is == to x and displays "correct" in a div if it is correct otherwise "incorrect"
}
So the main problems I am encountering is that
The html elements gets reset after submit. For example, the text fields becomes blank, the things I displayed in a div becomes blank. It just shows for a short period of time then gets reset
After that, the generated number becomes a different number I think the html page loads once more every time I click submit. And I don't like that to happen.
What I am having confusions on is the return statement on the onClick() attribute and how is it different on without return. See examples below:
CODE1:
<form onsubmit="return checkGuessedNumber();">
<input type="text"> // input for the number
<input type="submit"> // click if already sure of input number above
</form>
CODE2:
<form onsubmit="checkGuessedNumber();"> // notice no return here
<input type="text">
<input type="submit">
</form>
And finally if I'll just gonna put the checkGuessedNumber on <input type="submit" onclick="checkGuessedNumber();"> OR having a return before that.
Here's a live demo (click) of everything in this post.
First, don't use inline js (js functions in your html, like onclick). Read some of these results: Why is inline js bad?
Just for completeness, I'll explain how it works anyway:
This disables the submit nature of the button.
<input type="submit" onclick="return false;">
Now, if you want to use a function, you still need to produce the above result, so:
<input type="submit" onclick="return foo()">
and foo will have to return false, so that return foo() is the same as return false:
function foo() {
//do what you need to do;
return false;
}
I'll update this in a moment explaining the best practice, NOT using inline js.
The best element for a "button" is <button>, so I recommend that.
<button id="my-btn">Click Me!</button>
I gave it an id so that we can easily identify it in javascript. There are plenty of other ways to get element references, but that's another topic. Now, in javascript:
//get the element reference
var myBtn = document.getElementById('my-btn');
//this will make the button call function "foo" when it is clicked.
myBtn.addEventListener('click', foo);
function foo(event) {
//do whatever you want
}
If you assign an event listener to an element that has a default behavior, you can prevent the default behavior like this:
//the "event" object is automatically passed to the event handler
function foo(event) {
event.preventDefault();
//do what you want here
}

independently working div in Jquery

I am trying to make an independently working div which has a form inside of it.
I use jquery to calculate the price of a product depending of the user's selections in the form. However the user is able to add multiple items in his 'cart' so the form is duplicated to another div. The problem is that the calculation pattern can't separate these two divs and the calculation will be incorrect. The form is also interactive so it will be generated by the user's input. This is really complex set and renaming every variable by the 'product number' doesn't sound really efficient to me.
I'm kind of stuck here and i don't really know how to solve this problem. I had an idea that what if I put an iframe inside of the div and load my form and its calculation script inside of it, and then use post command to transfer the price of the product to the 'main page' to calculate the total price of all of the products the user wanted.
However it seems that jQuery scripts doesn't work independently inside of these iframes, they still have connection so they broke each other.
i will appreciate any kind of suggestions and help to solve this matter, thank you!
here's the code so far
Heres the body
var productNumber = 1;
<div id="div_structure">
</div>
<button id="newProduct" >Add new product</button><br \>
add new item
<!-- language: lang-javascript -->
$('#newProduct').click(function ()
{
$('<div id="productNo'+productNumber+'">')
.appendTo('#div_structure')
.html('<label onclick="$(\'#div_productNo'+productNumber+'\').slideToggle()">Product '+productNumber +' </label>'+
'<button onclick="$(\'#product'+productNumber+'\').remove()">Remove</button>');
$('<div id="div_product'+productNumber+'" style="display: none;">').appendTo('#product'+productNumber+'');
$('<iframe src="productform.html" seamless frameborder="0" crolling="no" height="600" width="1000">').appendTo('#div_product'+productNumber+'');
productNumber++;
});
it also has a function that allows the user to remove the inserted div.
Here's just few lines from the productform
$(document).ready(function()
{
$('#productCalculation').change(function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
<form id="productCalculation">
<div id="div_productShape" class="product1">
<h1>Select the shape of the product</h1>
<input type="radio" name="productShape" value="r1">R1</input><br \>
<input type="radio" name="productShape" value="r2">R2</input><br \>
<input type="radio" name="productShape" value="r3">R3</input><br \>
</div>
.
.
.
</form>
I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable
There are two problems with this code: You say somewhere "I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable". This is because event handlers are attached to elements that are in the DOM at that specific moment. To get it to work for all elements, use event delegation:
$(document).ready(function()
{
$(document).on( 'change', '#productCalculation', function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
Your other question is "My question in a nutshell: Is there a way to restrict jquery to function only in certain div even though i use the same variable names in the second div ". You can use the this variable to access the element the click was invoked on. From this element you can traverse the DOM if needed, for example with .parent().
$('div').on( 'change', function( e ) {
console.log( $(this).val() );
} );

How to entirely disable/enable html elements?

I have several elements on the page. Something like:
<div id="el1"><div id="el2"><span id="el3">1</span><span id="el4">2</span></div><span id="el5">3</span></div>
I need to disable/enable any of them, using their ids.
<input type="radio" name="do" onclick="disable(document.getElementById('el4'));">
<input type="radio" name="do" onclick="enable(document.getElementById('el4'));">
What should be in disable() and enable() functions to really disable elements?
By "disable" I mean make it invisible for user, inaccessible by "id" and be restorable by "enable()" function.
Is it possible to turn elements on/off? Entirely, I mean.
function addEl4(){
var elem = document.getElementById('el2');
var newElem = document.createElement('div');
newElem.setAttribute('id', 'el4');
newElem.innerHTML = 2;
elem.appendChild(newElem);
};
function disable(elem){
var container = document.getElementById('el2');
container.removeChild(elem);
};
If you are talking in terms of removing the element from the page completely.
Then you can use the .removeChild() method..
Then need to append it to the div you are talking about..
Check Fiddle
You may use
document.getElementById('el4').style.display='none'; // hide
document.getElementById('el4').style.display=''; // show
You could define functions like this :
function hide(element){
element.style.display='none';
}
function show(element){
element.style.display='';
}
I really suggest not to use the enable and disable words, as they have other meanings (a disabled widget is one you can see but you can't change).
A better solution would be to define a css class
.hidden {
display: none;
}
and change the class in js :
document.getElementById('el4').classname='hidden'; // hide
If you want to completely remove an element, you may use removeChild :
var node = document.getElementById('el4');
node.parentNode.removeChild(node);
but it's almost never useful. Prefer to hide as is commonly done.

adjusting dynamic form to work with multiple sections

I have a script which dynamically adds rows to a form with default values:
$(document).ready(function() {
var defaults = {
'name[]': 'Name',
'email[]': 'Email',
'organisation[]': 'Organisation',
'position[]': 'Position'
};
var setDefaults = function(inputElements, removeDefault)
{
$(inputElements).each(function() {
if (removeDefault)
{
if ($(this).data('isDefault'))
{
$(this).val('')
.removeData('isDefault')
.removeClass('default_value');
}
}
else
{
var d = defaults[this.name];
if (d && d.length)
{
this.value = d;
$(this).data('isDefault', true)
.addClass('default_value');
}
}
});
};
setDefaults(jQuery('form[name=booking] input'));
$(".add").click(function() {
var x = $("form > p:first-child").clone(true).insertBefore("form > p:last-child");
setDefaults(x.find('input'));
return false;
});
$(".remove").click(function() {
$(this).parent().remove();
});
// Toggles
$('form[name=booking]').delegate('input', {
'focus': function() {
setDefaults(this, true);
},
'blur': function() {
if (!this.value.length) setDefaults(this);
}
});
});
For the following form:
<form method="post" name="booking" action="bookingengine.php">
<p><input type="text" name="name[]">
<input type="text" name="email[]">
<input type="text" name="organisation[]">
<input type="text" name="position[]">
<span class="remove">Remove</span></p>
<p><span class="add">Add person</span><br /><br /><input type="submit" name="submit" id="submit" value="Submit" class="submit-button" /></p>
</form>
I would now like to split the form into 2 sections, each of which can have rows added dynamically to it. The second section would only have spaces for name and email, so the form as a whole, before any additional rows are added, would look something like this:
But I'm not sure how to achieve this. Either I would create a separate form with a seperate script, and then would need to know how to submit the information from both forms together, or I would just have one form but would then need to work out how to add rows dynamically to each section.
Could someone help with this?
Thanks,
Nick
I've implemented this in a fully functional example here.
I cleaned up your code a little bit, but it's basically the same. The main addition is that I wrapped the inputs in a fieldset element (you could use a div just as well, but fieldset is the semantically correct element for grouping related input fields). Your 4-input section lives in one fieldset, and your 2-input section lives in another; the "Add Person" handler looks for the parent fieldset, clones the first child, and adds it into that fieldset. Conveniently, in your use case the defaults for the first fieldset are the same as those for the second fieldset, but it would be easy enough to set up multiple sets of defaults and pass them into the setDefaults function.
A few other changes to the code:
I split your setDefaults function into two different functions, setDefaults and removeDefaults - you weren't gaining anything by making them a single function, and splitting them makes the code more legible.
I used .delegate to assign the "Remove" handler - otherwise the "Remove" button wouldn't work for new input sets. I also created the "Remove" button with jQuery, rather than cloning it, because I assumed that it wouldn't make sense to include it for the first input set.
I used jQuery in a couple of places where you were using raw Javascript (e.g. getting and setting input values). I generally assume that jQuery is more reliable for cross-browser DOM access and manipulation, so if you're loading the library already there's rarely any point not using it for all but the simplest DOM functions.
I removed your .data calls, since you can get the same information by inspecting the class, and it's generally better to reduce complexity. It's possible that .hasClass('test') is slightly slower than .data('test'), but I don't think it should make any difference here.
Create one form. Put two divs inside of it. Have your script add/remove form elements to the appropriate div.
When you submit the form it should automatically submit all of the form elements in both divs since the divs are contained in the form.

Toggle effect problem when using for-loop in IE7

I'm a webdesigner that's trying to get the hang of JavaScript and jQuery, and I want to learn how to write shorter, more concise code - to avoid being ridiculed by the developers at work ;)
I have this snippet:
// toggle divs when checkbox is checked
$('.product-optional-checkbox1').click(function () {
$('.product-optional-toggle1').toggle('fast');
});
$('.product-optional-checkbox2').click(function () {
$('.product-optional-toggle2').toggle('fast');
});
$('.product-optional-checkbox3').click(function () {
$('.product-optional-toggle3').toggle('fast');
});
// hide divs
$('.product-optional-toggle1').hide();
$('.product-optional-toggle2').hide();
$('.product-optional-toggle3').hide();
...that I want to reduce using a for-loop, like this:
for( var i = 1; i < 4; ++i ) {
$('.product-optional-checkbox' + i).click(function () {
$(this).parent('div').find('div').toggle('fast');
});
$('.product-optional-toggle' + i).css({ display: 'none'});
};
It works fine in FF, however in IE7 it toggles twice. Anyone know who to solve a problem like this?
It's hard to say without seeing the HTML structure but maybe going to the parent then descendants is finding multiple divs?
In your example, you could replace:
$(this).parent('div').find('div').toggle('fast');
With code more similar to the original examples:
$('.product-optional-toggle' + i).toggle('fast');
However, it would be much better to ditch all the numbers and just use a class of .product-optional-checkbox. This way you can add a click function to all elements of that class in one go and avoid the loop:
$('.product-optional-checkbox').click(function () {
// do stuff using $(this)
});
Given that your click events seem to be tied to checkboxes (based on the class names you have in your example), why not actually provide code to handle click events for those checkboxes? For example:
<div id='my_group_of_checkboxes'>
<input type="checkbox" id="checkbox1">
<input type="checkbox" id="checkbox2">
...
</div>
And your jQuery code is then stripped down to three lines:
$('#my_group_of_checkboxes :checkbox').click(function(){
$(this).parent('div').hide('fast');
});
You also seem to need to hide the <div> elements related to each checkbox:
$('div[class^="product-optional"]').hide();
Although ID selectors would be a better option here and, depending on their position within your page, you may even be able to get away with something like:
$('#my_container_div_id div').hide();
If you can post some of your HTML, that might help provide more accurate answers as well.

Categories

Resources