Recommended method of adding/storing/constructing HTML controls dynamically - javascript

Is there a recommended method of dynamically adding/storying/constructing HTML controls to dynamically added elements on a HTML page?
Basically, I have a widget based system I'm working on, where the user chooses the widgets s/he wants to have displayed. S/He can have as many or as little widgets on the screen as required, and they can also have the same widget many times displayed at once.
The script for that looks something like this:
success: function( widget_shell )
{
if( widget_shell.d[0] ) {
$(".column").empty();
var i = 0;
for ( i = 0; i <= widget_shell.d.length - 1; i++ ) {
var $widget = $("<div class='widget widget_" + widget_shell.d[i].widget_id + "'>").appendTo( $(".column_" + widget_shell.d[i].column_id) );
$("<div class='widget_header widget_header_" + widget_shell.d[i].widget_id + "'>").appendTo( $widget );
$("<div class='widget_sub_header widget_sub_header_" + widget_shell.d[i].widget_id + "'>").appendTo( $widget );
$("<div class='widget_content widget_content_" + widget_shell.d[i].widget_id + "'>").appendTo( $widget );
$("<div class='widget_footer widget_footer_" + widget_shell.d[i].widget_id + "'>").appendTo( $widget );
}
each .widget will need controls such as buttons, textboxes etc in the .widget_sub_header section. What is recommended way of doing that? "That" as in, how to get further HTML controls into the .widget_sub_header. Add the HTML for the controls in the database? Somehow connect to an external file containing the HTML controls and add them in? or some other method?
I guess my 2 main concerns are:
Easy maintenance
Server resource friendly

jquery-ZenCoding is easy way to create some nested html elements :
https://github.com/zodoz/jquery-ZenCoding
exemple :
$.zen("div#widget>ul.instrument>(li{piano}+li{guitare}+li{flute})");

What about something like this? I suggest instead of adding a class with the id, add an actual id on the parent, that's all you need to target children elements.
function createWidget( id ) {
var widget = [
'<div class="widget">',
'<div class="widget_header"></div>',
'<div class="widget_sub_header"></div>',
'<div class="widget_content"></div>',
'<div class="widget_footer"></div>',
'</div>'
].join('');
return $( widget ).attr( 'id', 'widget_'+ id );
}
$.each( widget_shell.d, function( i,v ) {
$('.column_'+ v.column_id)
.append( createWidget( v.widget_id ) );
});

I'd add a small template engine. There is a very simple one made by John Resig that I use frequently at:
John Resig micro-templating
Chances are you'll be doing this sort off thing again so a template engine is pretty handy to have lying around.
If you don't like that one you can have a look at a great resource of micro-templates at:
Microjs templates

I've used to work on a similar widget system.
This is how I've managed it :
Have a hidden div container that holds all the template elements.
<div id='widget-templates'>
<div class='widget1-id' >
<input type='text' class='ytSearch'/>
<button id='btnYtSearch'>Search</button>
</div>
<div class='widget2-id'></div>
...
<!-- etc etc -->
...
</div>
I used an object called widgetFactory which holds all the javascript code needed to create a widget from the template.
var widgetFactory = {
youtubeSearch: function(data){
var $generatedWidget = $('#widget-templates > .widget1-id').clone();
// prepopulate with some data if requested (usually when binding data to an already existing widget)
// ...
// attach events
// ...
return $generatedWidget;
}
}
Then with external javascript logic, I would save just the widget data (as JSON) and widget type into the database.
tbl_widgets(id, name, type)
tbl_user_widget(user_id, widget_id, data)
Note that this is just from my memory and there probably is a much more profound solution, but i hope it could shine any new idea on your user customizing widget system.

Related

How can I access the attributes of an element cloned via jquery within a javascript function?

I need to permit my users to dynamically create new categories and then branches and sub-branches within categories. To facilitate this I've built an html table that contains multiple nested tables. Each nested table is surrounded by its own div. The base category displays on the left and branches and sub-branches grow out to the right.
To allow the user to create the branches I call jquery clone() within a javascript function. Like this:
function elementCopy(targetTable, targetDiv)
{
var clone = $('.' + targetTable).clone(true).appendTo($("." + targetDiv));
}
This creates the requested clone and places it in the correct table. But I have not discovered how to access the clone's attributes to edit them.
In a previous iteration of this functionality, I called jquery clone() from within document.ready().
$('button[name="newTasks"]').click(function()
{
//Create the cloned block and keep the event listener so the newly displayed plus icon
// also creates a new block.
var clone = $(".tasksTable").eq(0).clone(true).appendTo(".tasksDiv");
//
clone.find( ".tasksTable" ).attr('name', 'Tasks' + '_' + countTaskBlocks);
});
I abandoned this iteration because I was unable to send parameters to the document.ready() version. Without the parameters, I could not position the clone within its proper table and sub-table. However, this version DID allow me to edit the clone's attributes.
This line edits all of the elements with the same class name:
$('.' + targetTable).attr('class', 'tasksTable' + '_' + countTaskBlocks);
How can I edit the 'name', 'id', and 'class' attributes of the specific clone created in the call to 'elementCopy' function?
JQuery methods are chainable, so you can do something like this.
function elementCopy( targetTable, targetDiv ) {
var clone = $( '.' + targetTable )
.clone( true )
.appendTo( $( "." + targetDiv ) )
.attr( {
'name': 'Tasks' + '_' + countTaskBlocks,
'id': 'Tasks' + '_' + countTaskBlocks,
'class': 'Tasks' + '_' + countTaskBlocks
} );
return clone;
}
Check out the demo below.
$('a').clone( true ).appendTo( $('body') ).attr( { 'class': 'Goodbye', 'href': 'http://www.example.com' } );
a::before { margin-right: .5em; content: attr(href); } a::after { margin-left: .5em; content: attr(class); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a class="hello" href="#">Link</a>
Search the children of the DIV you appended the clone to by the class name of the element you cloned.

Cordova / Phonegap -- jquery not executing Javascript on <div> load

I have a single page (index.html) document for phonegap that displays different "pages" when each new "div" is referenced. For example:
<div class="upage ui-page-theme-a" id="view_history" data-role="page" >
I am wanting to have a javascript function automatically execute via Jquery when the "div" is displayed. My code is below.
However I can't get anything to cause it to execute on div load. I can put an "onclick" call in the HTML, ie:
<a onclick="listMeasurements()" class="button" style="" data-role="button">List
Current Measurements</a>
and that works fine, but for some reason I can't get it to execute just from loading. Any insights would be greatly appreciated.
Sorry in advance if I'm overlooking something obvious.
$('#view_history').on('pagecontainershow', listMeasurements()) ;
// I've also tried bind() etc.
function listMeasurements() {
"use strict" ;
var fName = "listMeasurements():" ;
var measurements_recorded = window.localStorage.length;
$("#Measurements").html("<strong>" + measurements_recorded + "</strong>
measurement(s) recorded");
// Empty the list of recorded tracks
$("#measurement_list").empty();
// Iterate over all of the recorded tracks, populating the list
for(var i=0; i<measurements_recorded; i++){
$("#measurement_list").append("<li data-uib=\"jquery_mobile/listitem\" data-ver=\"0\"><span>" + window.localStorage.key(i) + "</span></li>");
}
$('#measurement_list').listview().listview('refresh');
}
From jQuery Mobile 1.4.2 the events have changed & don't work with a binding to a specific page like you have done i.e this -
$('#view_history').on('pagecontainershow', listMeasurements()) ;
won't work anymore.
You need to put a binding something like this on document -
function showSomeLove() {
alert("What is love, baby don't hurt me, no more!");
}
$(document).on( "pagecontainershow", function( event, ui ) {
var currentPageId = $( ":mobile-pagecontainer" ).pagecontainer( "getActivePage" ).attr('id');
if(currentPageId == 'page-a') {
/*Execute or Call your function here*/
showSomeLove();
} else if(currentPageId == 'page-b') {
/*do something else*/
}
});
Here's a WORKING DEMO - http://codepen.io/nitishdhar/pen/gnqFH
For more details read the pagecontainer widget - http://api.jquerymobile.com/pagecontainer/

Make a javascript function instead variables

Here I have a code that create a sidebar:
var side_bar_html = "<a href='javascript:google.maps.event.trigger(gmarkers[" + parseInt(gmarkers.length - 1) + "],\"click\");'>" + place.name + "</a><br>" + '<div class="raty" />' + "</br>";
$(side_bar_html).appendTo('#side_bar').filter('.raty').raty({
score : place.rating,
path : 'http://wbotelhos.com/raty/lib/img'
})
How I can create a function from this code to create a sidebar with function ...
So something like that:
function Create_a_sidebar_and_put_it_into_ID#sidebar () {
//for every marker to return html string
return "<div class='element'>"+place.name+"</br>"+place.rating+"</div>" + etc...
Becouse I have a problem with creating html, I dont know what to append where and I dont have contol over that
Is it possible?
If I'm understanding your question correctly, you're asking how you can take your first code block that creates a rating for a certain place, and refactor it so that you can arbitrarily create sidebars for places at will. So that's how I'll approach this answer.
As #Sime Vidas mentioned, you can start by taking the code that creates the sidebar itself and making that a function such as that below. I've modified the function a bit to take the javascript out of the href attribute (which is generally considered a bad practice) and replaced passing an html string into $.fn.init (which I've found steeply degrades performance) with using DOM methods to create elements. You also don't need the <br /> after your a element because divs by default are block elements.
function createSidebar(place) {
var $sidebarLink = $(document.createElement('a'));
var $raty = $(document.createElement('div'));
$sidebarLink.attr('href', '#').text(place.name).click(function(evt) {
evt.stopPropagation();
google.maps.events.trigger(gmarkers[parseInt(gmarkers.length - 1, 10)], 'click');
});
$raty.addClass('raty').raty({
score: place.rating,
path: 'http://wbotelhos.com/raty/lib/img'
});
return $([$sidebarLink, $raty]);
}
Now you can do things like
var $sidebar = $('#side_bar');
places.map(createSidebar).forEach(function($sidebarPart) {
$sidebar.append($sidebarPart);
});
Sorry if I'm off track with answering your question, but I think this is what you were asking. If not feel free to leave a comment and we can talk about it more!

Use find data attribute to add class

I am attempting to create a menu with unique data attributes attached to each navigation item. On click I want that navigation item to find the section with the same attribute and add a class to it. This is the code I am using thus-far (it is part of a much larger script).
var $el = $( '#bl-main' ),
$sections = $el.children( 'section' ),
$navItems = $( 'nav > a' );
$navItems.on( 'click', function( event ) {
$el.addClass( 'bl-expand-item' );
$navItems.find("[data-section='" + current + "']");
$sections.find("[data-section='" + $navItems + "']").addClass( 'expand expand-top' );
} );
This is the script it based off of. http://tympanus.net/Development/FullscreenLayoutPageTransitions/
The idea is to add a separate floating navigation to link to the different sections (instead of the direct click to expand that is current implemented). The current script I added only seems to refresh the page on click. I couldn't get it to work in a fiddle at all so the original script im sure will work. The section pasted about I have added in on my local files.
On the first line, when you try to call for a <section> element, you are missing the jQuery shorthand before the parentheses. That should fix your script.
So it should look like:
var $section = $('section');
That should have you all set to continue!
First off you are missing your $ in the first variable assignment. var $section = $('section')
After that it is difficult to tell what you're trying to do without seeing your markup, but I'll take a stab at it.
It appears that you are trying to filter the $section object by the data-section attribute of the nav > a element. This code should do just that:
var $section = $( 'section' ), $navItems = $( 'nav > a' );
$navItems.on( 'click', function( event ) {
var $navSection = $section.filter("[data-section='" + $( this ).data( 'section' ) + "']");
$navSection.addClass( 'expand expand-top' );
return false;
} );
Notice that we are filtering all of the sections by the value of the clicked anchor's data-section attribute.
If that doesn't help, add your markup so we can see your structure.

How to simplify code to add new elements to a page with jQuery?

I've written the following code to add a clickable "link button" to a section of my page.
var linkButtonHtml = "<a data-makeId='" + makeId + "' href='javascript:expandMake(" + makeId + "," + categoryId + ")'>+</a> " + makeName;
var divHtml = "<div style='display:none' class='models' data-makeId='" + makeId + "'></div>" + "<br/>";
html += linkButtonHtml + divHtml;
$('#linkDiv').html(html);
The code works fine, but it's ugly and difficult to read with all the string concatenation.
As you can see, I am building anchor elements and div elements with string concatenation. The target of my anchor element is a javascript function invocation with two arguments. Is there a good jQuery way to improve the readability of this code?
I'm not sure if this really improves readability is here is a 100% jQuery solutions:
$(html)
.append(
$('<a />')
.attr('data-makeId', makeId)
.attr('href', 'javascript:void(0);')
.click(function(event)
{
// Prevent clicking the link from leaving the page.
event.preventDefault();
expandMake(makeId, categoryId);
})
.text('+'))
.append(
document.createTextNode(makeName)
)
.append(
$('<div />')
.addClass('models')
.attr('data-makeId=', makeId)
.hide());
Where "html" in $(html) is the html variable you have in your sample.
jQuery offers an option for a second argument when creating elements.
var linkButton = $('<a>',{'data-makeId':makeId,
href:'#',
click:function(){expandMake( makeId, categoryId )},
text:'+'
});
var div = $('<div>',{ style:'display:none',
'class':'models',
'data-makeId': makeId
})
.after('<br>');
$('#linkDiv')
.empty()
.append(html)
.append(linkButton)
.append( makeName )
.append(div);
EDIT: Fixed an issue where makeName was not appended.
Only real way is either abstracting some of your tag generation or spread the script out a little to make it more readable : http://jsfiddle.net/3dYPX/1/
Your also using jQuery so you might want to consider changing the way you trigger the javascript. Try looking into the .live() event. (Ill just get an example up, not that its very important)
Using live event for unobtrusive javascript:
http://jsfiddle.net/3dYPX/2/
It is all being done inside of the onLoad event at the moment, just to use as an example.
Use a template library such as
jQuery Templates instead of inlining
HTML.
Instead of using "javascript:" URLs, attach event handlers to the generated DOM fragments.
Refrain from using inline styles.
Something like:
$('#linkDiv')
.empty()
.append($.tmpl(myTemplate, {
makeId: makeId,
makeName: makeName,
categoryId: categoryId
}))
.click(function () {
var makeId = $(this).attr("data-makeId");
if (makeId) {
expandMake(makeId, $(this).attr("data-categoryId"));
}
});
Where myTemplate has the content:
${makeName}
<div class="models" data-makeId="${makeId}"></div>
Instead of using an inline style to initially hide the models, hide them all with a general CSS rule, and then selectively show them with a class:
.models { display: none }
.models.shown { display: block }
Just add the "shown" class to show a certain block of models.
Here you go:
$('#linkDiv').empty().append([
$('+').data('makeId', makeId).click(function(e) {
e.preventDefault();
expandMake(makeId, categoryId);
})[0],
$('<span>').text(makeName)[0],
$('<div class="models">').data('makeId', makeId).hide()[0],
$('<br>')[0]
]);
Live demo: http://jsfiddle.net/cncbm/1/
Consider this:
$('#linkDiv').data({'makeId': makeId, 'categoryId': categoryId}).empty().append([
$('+').click(expandMake)[0],
$('<span>').text(makeName)[0],
$('<div class="models">').hide()[0]
]);
So, you define that data-stuff on the parent DIV (the common parent) and you re-factor the expandMake function so that it reads those data-values from the parent DIV instead of passing them as arguments.

Categories

Resources