Stuck on structure of this jquery plugin - javascript

I'm practicing jquery plugins and need help finishing this one, especially the outside skeleton.
Let's say I have this markup
<div class="tochange"></div>
<div class="tochange"></div>
<div class="tochange"></div>
and I want the plugin to add to div.tochange this markup
<div class="root">Root</div>
so that it's
<div class="tochange">
<div class="root">Root</div>
</div>
Then if the root is clicked, replace it with 2 divs so the markup looks like this
<div class="tochange">
<div class="child">Child</div>
<div class="child">Child</div>
</div>
If the child is clicked, it goes back to parent
<div class="tochange">
<div class="root">Root</div>
</div>
I'm following documentation but I don't know if I need methods for this. My guess is that I do but I can't finalize the structure of this plugin in my head. This is my first plugin and I thought a practical idea of my own is the best way to learn, but I'm stuck. Can someone who's done this before set me on the right track. I'm a little lost on this.
(function($){
$.fn.sample = function(options) {
var settings = {
'possibleparam1' : 'value1',
};
var methods = {
init: function( options ) { },
tochildren : function( ) { },
toparent : function( ) { },
};
return this.each(function() { /*(i) {*/
// If options exist, merge them with our default settings
if (options) {
$.extend(settings, options);
}
// plugin code goes here
});
};
})( jQuery );

You can use several methods:
Try $("field_name").update("New text"); or $(this).replaceWith.
I have something like this in my site and I'm using: (don't mind the func name - its my ajax callback)
function setOutput()
{
if(httpObject.readyState == 4)
{
document.getElementById('photos').innerHTML = "<div id=\"gallery\" clas.....</ul></div>";
}
}

Related

Using the listChanged parameter

I'm using the jQuery UI plugin fieldchooser and I want to know whenever the list changes so that I can update it in the database. The plugin has a listChanged function, but I'm not sure how to make it work. I working off this example:
<script>
$(document).ready(function () {
var $sourceFields = $("#sourceFields");
var $destinationFields = $("#destinationFields");
var $chooser = $("#fieldChooser").fieldChooser(sourceFields, destinationFields);
});
</script>
<div id="fieldChooser" tabIndex="1">
<div id="sourceFields">
<div>First name</div>
<div>Last name</div>
<div>Home</div>
<div>Work</div>
<div>Direct</div>
<div>Cell</div>
<div>Fax</div>
<div>Work email</div>
<div>Personal email</div>
<div>Website</div>
</div>
<div id="destinationFields">
</div>
</div>
Elsewhere on stack overflow, I found this example:
$chooser.on("listChanged",function(event,selection,list){
//event <- The jQuery event invoking the callback.
//selection <- The field (or set of fields) which has moved.
//list <- The field list to which the selection has moved.
alert("listChanged");
}
and have tried this, without success:
$chooser.on("listChanged",function(mouseup,sourceFields,destinationFields){
alert("listChanged");
});
I'd appreciate any suggestions on making this work. Thanks.
With correct code, this works as expected.
Working Example: https://jsfiddle.net/Twisty/v2Lrm9wq/2/
JavaScript
$(function() {
var $chooser = $("#fieldChooser").fieldChooser();
var $sourceFields = $("#sourceFields").children();
$chooser.getSourceList().add($sourceFields);
$chooser.on("listChanged", function(event, selection, list) {
alert("listChanged");
});
});

Refresh child in dynamically created nested accordion

I am trying to append items to a nested accordion dynamically when the user clicks a button. I am using the following code to create a nested accordion:
$(".accordion").accordion({
collapsible: true,
autoHeight: false,
animated: 'swing',
heightStyle: "content",
changestart: function(event, ui) {
child.accordion("activate", false);
}
});
var child = $(".child-accordion").accordion({
active:false,
collapsible: true,
autoHeight: false,
animated: 'swing'
});
In order to do this, I have found that I need to refresh the accordion using the following:
$('.accordion').accordion("refresh");
My problem is that when I try to refresh the inner accordion using:
$('.child-accordion').accordion("refresh");
I get the following:
Error: cannot call methods on accordion prior to initialization; attempted to call method 'refresh'
When I inspect the div that should be refreshed it has the following ids/classes:
DIV#shelf0sections.child-accordion.ui-accordion-content.ui-helper-reset.ui-...
I tried using the selector:
$('#shelf0sections .child-accordion').accordion("refresh");
instead which doesn't give an error, but nothing happens visually.
jsFiddle: http://jsfiddle.net/Mw9SA/3/
(Note that the first element in the list is just an example to see the nested accordion working, If you try to add sections to it, it won't work. Use the '+Shelf' button, then open the new accordion and use the '+Section' button.)
How about a more modular approach?
Fiddle or it didnt happen: http://jsfiddle.net/Varinder/24hsd/1/
Explanation
The idea is ( the same ) to create a brand new accordion element on the fly with correct events attached and appended somewhere in the DOM.
It's generaly more managable to have repeater HTML markup abstracted away in a template somewhere in DOM and use JS to reference it rather that building it from a string.
Heres the accordion template in the markup:
<div class="template">
<div class="accordion">
<h3 class="accordion-title">accordion title</h3>
<div class="accordion-content">
accordion content
</div>
</div>
</div>
Heres the full HTML markup - just in case:
<div class="page">
</div>
<div id="addShelf" class="button">+ Shelf</div>
<div id="addSection" class="button">+ Section</div>
<div class="template">
<div class="accordion">
<h3 class="accordion-title">accordion title</h3>
<div class="accordion-content">
accordion content
</div>
</div>
</div>
JS
Starting off by storing different accordion configurations:
var shelfConfig = {
collapsible: true,
autoHeight: false,
animated: "swing",
heightStyle: "content"
}
var shelfSectionConfig = {
active:false,
collapsible: true,
autoHeight: false,
animated: "swing"
}
Kepping a track of current accordion number and current accordion section number ( number of sections inside last accordion ) - might come in handy if you require a feature to remove an accordion shelf
var currentShelfNumber = 0;
var currentShelfSectionNumber = 0;
Chaching DOM elements, notice reference to the tempalte div
var $page = $(".page");
var $accordionTemplate = $(".template").children();
var $addSection = $("#addSection");
var $addShelf = $("#addShelf");
Creating a helper function that simply returns a cloned copy of the accordion template from the DOM
function getAccordionTemplate() {
return $accordionTemplate.clone();
}
Main function generateAccordion - it takes two arguments, accordionNumber to append current number in titles etc and accordionType to find out which accordion configuration to use.
With those parameters it will return a brand-spanking-new accordion with appropriate events attached which can then be append to the DOM
function generateAccordion( number, accordionType ) {
var $accordion = getAccordionTemplate();
var accordionTitle = "twerking bieber?";
if ( accordionType == "shelf" ) {
accordionTitle = "Shelf " + number;
} else {
accordionTitle = "Shelf Section";
}
$accordion.find("h3").text( accordionTitle );
var $accordionWithEvents = attachAccordionEvents( $accordion, accordionType );
return $accordionWithEvents;
}
Notice the call to another function attachAccordionEvents as the name suggests - this fella will attach events to the accordion element.
function attachAccordionEvents( $accordionElement, accordionType ) {
if ( accordionType == "shelf" ) {
$accordionElement.accordion( shelfConfig );
} else {
$accordionElement.accordion( shelfSectionConfig );
}
return $accordionElement;
}
Another helper function which makes sure "add section" button doesnt show up if there is no accordion shelf for it to work on
function manageSectionButton() {
if ( $page.children().length > 0 ) {
$addSection.show();
} else {
$addSection.hide();
}
}
Finaly events and logic:
$addShelf.on("click", function(e) {
e.preventDefault();
var newShelfNumber = currentShelfNumber + 1;
var $shelfElement = generateAccordion( newShelfNumber, "shelf" );
currentShelfNumber = newShelfNumber;
$page.append( $shelfElement );
manageSectionButton();
});
$addSection.on("click", function(e) {
e.preventDefault();
var newShelfSectionNumber = currentShelfSectionNumber + 1;
var $shelfSectionElement = generateAccordion( newShelfSectionNumber, "section" );
var $activeShelfElement = $page.children().last().find(".accordion-content");
$activeShelfElement.append( $shelfSectionElement );
});
... And thats about it.
Hope this helps,
Cheers

Replacing widget element with a newly constructed DOM structure

<script>
(function( $ ) {
$.widget( "my.dropbox", {
errorText: function(text) {
$(this.element).next().html(text);
},
_create: function() {
var id = $(this.element).attr("id");
var customDropbox = $(
"<div class='form-group'>"+
"<label for='"+id+"'>"+getLabelFor(id)+"</label>"+
"<select id='"+id+"'></select>"+
"<div class='errors'></div>"+
"</div>"
);
customDropbox.attr("id", id);
$(this.element).replaceWith(customDropbox); // This removes original element from DOM
populateOptions(id);
},
});
}( jQuery ));
$(document).ready(function(){
$("#field1").dropbox(); //blank input field turns into a select with a label, populated options e.t.c..
$("#button1").on("click", function(){
$("#field1").dropbox("errorText", "This is a validation error message"); //throws an error saying dropbox is not initialized
});
});
</script>
<html>
<body>
<input id="field1" />
<button id="button1">Press me</button>
</body>
</html>
So I want a widget with public methods that will replace the original element with all the widget data associated with it. The problem with the above code is that the <select..> element is just a DOM element and if you call .dropbox(..) on it, it will say the widget is not initialized. Is there a way to make the select element into the widget object with the .errorText() method? All widget examples online add stuff around the original element but never replace it. As for the bigger picture, I'm trying to make a generic tool to configure forms dynamically. It's going to be all <input id="..."> in html but then javascript will query a database, get configuration for the field and turn it into a dropbox, checkbox or, say, a date picker with all the labels, validation, and other bells and whistles.
There is more than one issue with your widget code. I'll try to summarize them:
1. Copy the data
You're not copying the data to the newly created customDropbox, so before
this.element.replaceWith(customDropbox);
you should copy the data:
customDropbox.data(this.element.data());
Now the widget will remember that it was initialized.
2. this.element is gone
After
this.element.replaceWith(customDropbox);
you should update this.element so that it points to the newly created customDropbox:
this.element = customDropbox;
3. errorText message takes wrong element
Since the widgets element (this.element) is now pointing to the <div class='form-group'></div> element, the errorText function must be slightly modified to:
this.element.find(".errors").html(text);
4. id should be unique
Now, both the wrapper <div> and the <select> have the same id, which is not allowed in HTML so remove the one on the <select> tag. Luckily, <label> can work without the for attribute, just write it like this:
<label>labelForId <select></select></label>
Then to get the <select>-element, use this.element.find("select") in the widget.
Side note
`this.element` is already a jQuery element, so no need for the additional `$()` wrapping.
See this jsFiddle
function show(){
$("#field1").input({....});
}
function hide(){
$("#field1").input("hide");
}
<button onclick="show()">show</button>
<button onclick="hide()">hide</button>
i think to replace the origin element which initial dropbox() is not a good solution,
because this will force you to rely on the implemention details of jQuery ui factory,
it is easy to make a mistake or introduce bugs, sometimes harder for other people to understand your code
if jquery ui factory change the implemention in the future, you have to modify all your code to make it work
(sorry for my limit understand of jquery ui)
i think we can put the <input/> into a container and initial dropbox() on the container which inturn
replace <input/> with <select> datepicker ..etc.. we can build modules easily by doing so:
<form>
<div class="dropbox"><label for="someID">aaaaaa</label><input id="someID"/></div>
<div class="datepicker"></div>
<div class="othermodule"></div>
</form>
js:
$(".dropbox").dropbox(); // init dropbox you defined
$(".datepicker").datepicker(); // ...
$(".othermodule").othermodule(); // ...
$(".dropbox").dropbox("errorText", "error"); // invoke it smoothly
here is a simple demo: http://jsfiddle.net/m4A3D/
#Wouter Huysentruit's answer provides a list of good suggestion for me
<form>
<div class="dropbox">
<label for="someID">aaaaaa</label>
<input id="someID"/>
</div>
<div class="datepicker"></div>
<div class="othermodule"></div>
</form>
<button id="button1">Press me</button>
<script>
(function ($){
$.widget("my.dropbox", {
_create: function () {
var $input = this.element.find("input");
var sID = $input.attr("id");
var $select = $("<select>");
$select.attr("id", sID);
$input.replaceWith($select);
this.element.append("<div class='errors'></div>");
}, // end _create()
errorText: function (text) {
this.element.find(".errors").text(text);
} // end errorText()
});
}(jQuery));
$(".dropbox").dropbox();
$("#button1").click(function () {
$(".dropbox").dropbox("errorText", "this is error");
});
</script>

Events running multiple times after adding new content to the DOM

I have issue with my plugin, because I need a way to update if new elements are added to the DOM I added a update methods,
if I start the plugin all goes well, everything works perfect, not issues, no error, but once I add a new elelemnt(div with class box)
to the DOM things goes wrong, the update works, but the click events seem to fire multiple times now, so if I add a new element the event runs
twice, if I add 2 elements to the DOM, the events runs 3 times....and so on. I am not that good at Js, so I am stuck at this, I have tried a lot but nothing seems to work.
Elements that are newly added work fine, but if I add some more new elements they will have the same issues.
I added below a small preview, as my plugin is custom an big I only posted the parts that have issues(made them easy to understand).
The update method is needed, new elements(.box) need to be updated(add new code to the .box)
the HTML code
<div id="container">
<div class="box">
link 1
link 2
<div>content goes here...</div>
</div>
<div class="box">
link 1
link 2
<div>content goes here...</div>
</div>
<div class="box">
link 1
link 2
<div>content goes here...</div>
</div>
</div>
inline script
$('#container').myplugin01();
$('#somelink').click(function(e){
$('#container').append('<div class="box">link 1link 2<div>content goes here...</div></div>');
$('#container').myplugin01('update');
});
the plugin
;(function($, window, document, undefined){
//"use strict"; // jshint ;_;
var pluginName = 'myplugin01';
var Plugin = function(element, options){
this.init(element, options);
};
Plugin.prototype = {
init: function(element, options){
this.elm = $(element);
this.options = $.extend({}, $.fn[pluginName].options, options);
// example 1: animation
$('#container').children('.box').on("click", ".link1", function(e){
$(this).parent().children('div').animate({height: 'toggle'},400)
});
// example 2: wrapping
$('#container').children('.box').on("click", ".link2", function(e){
$(this).parent().wrap('<div class="wrapped"></div>')
});
this.update();
},
update: function(){
$('#container').children('.box').addClass('someclass');
// more code here...
}
};
$.fn[pluginName] = function(option) {
var options = typeof option == "object" && option;
return this.each(function() {
var $this = $(this);
var data = new Plugin($this, options);
if(!$.data($this, pluginName)){
$.data($this, pluginName, data);
}
if(typeof option == 'string'){
data[option]();
}
});
};
/**
* Default settings(dont change).
* You can globally override these options
* by using $.fn.pluginName.key = 'value';
**/
$.fn[pluginName].options = {
name: 'world'
};
})(jQuery, window, document);
If you bind events more than once this problem will occur.
// inline script
$('#container').myplugin01();// binding first time
$('#somelink').click(function(e){
$('#container').append('<div class="box">link 1link 2<div>content goes here...</div></div>');
$('#container').myplugin01('update');// binding second time
// We suggest you to unbind here and rebind it.
});

How to implement multiple tinyscrollbars from a class?

I'm trying to implement multiple scrollbars with the plugin Tinyscrollabr.js
http://baijs.nl/tinyscrollbar/
To implement the scrollbars, i use a function scrollify like in this article :
http://www.eccesignum.org/blog/making-tinyscrollbarjs-easier-to-implement
HTML :
<ul id="myList">
<li id="scrollbar1" class="col">
<h2>Title 01</h2>
<div class="scrollBox"><!--Scrollable Content here--></div>
</li>
<li id="scrollbar2 class="col">
<h2>Title 02</h2>
<div class="scrollBox"><!--Scrollable Content here--></div>
</li>
<li id="scrollbar3 class="col">
<h2>Title 03</h2>
<div class="scrollBox"><!--Scrollable Content here--></div>
</li>
</ul>
Javascript :
function scrollify(element,options) { // '#element', {list:of,key:values}
var opt = options || {}
$(element).children().wrapAll('<div class="viewport"><div class="overview"></div></div>');
$(element).prepend('<div class="scrollbar"><div class="track"><div class="thumb"><div class="end"></div></div></div></div>');
$(element).tinyscrollbar(options);}
$scrollbar1 = $('#scrollbar1 .scrollBox') ;
$scrollbar2 = $('#scrollbar2 .scrollBox');
$scrollbar3 = $('#scrollbar3 .scrollBox');
$scrollbar4 = $('#scrollbar4 .scrollBox');
$(function() {
scrollify($scrollbar1);
scrollify($scrollbar2);
scrollify($scrollbar3);
scrollify($scrollbar4);
})
I would to make this more simple.
For example, i would to be able to make this :
$(function() {
scrollify('.scrollBox');
})
But tinyscrollbar need an id. With a class, it's load the first scrollbar and not the others. Firebug return this error message "f.obj[0] is undefined"
Sorry if my question is stupid, but how can I do for applying tinyscrollbar to a list of elements with a class ?
And then, after some actions how to update all this scrollbars with the function $allScrollbars.tinyscrollbar_update();
Thanks for help, I'm just beginning with javascript and i'm trying to learn.
I would count the number of elements with the class:
var scrollCount = $(".scrollbox").size();
Then use an iterating loop to call each of your IDs:
for (i=0; i<5; i++) {
scrollify($('#scrollbar' + i));
}
Also I would recommend using DIVs instead of the list setup you have, use the example from the link you shared as a starting point :)
Thanks KneeSkrap3r for your answer. It's a good solution to make this but i'm trying to do something in the case i' don't know the numbers of element to scroll.
I think I've found with something like this (it's a part from the first jquery plugin i'm trying to do ) where $el is all elemnts with the class"scrollbox".
$el.each(function(index)
{
var $scrolls = $(this);
function scrollify(element,options)
{ // '#element', {list:of,key:values}
var opt = options || {}
$(element).children().wrapAll('<div class="viewport"><div class="overview"></div></div>');
$(element).prepend('<div class="scrollbar"><div class="track"><div class="thumb"><div class="end"></div></div></div></div>');
$(element).tinyscrollbar(options);
}
scrollify($scrolls);
// Update heights
$(window).resize(function()
{ $scrolls.tinyscrollbar_update('relative');
});
})
Like this, it's seems to work but i don't know if i'm using good practice of javascript.
For the Html markup, I told the li elements for div, it's better for the semantic.
Thanks for tips ;-)

Categories

Resources