jQuery Plugin breaks with this.each - javascript

I'm writing a plugin and trying to wrap the function inside of an each method but it breaks the plugin. If the block content is not wrapped within "this.each" plugin works. I understand that in order to pass multiple selectors I need to "return this.each" or not? I also want to eliminate the need to use the selector inside the plugin e.g "#the_lead", instead use "this".
(function($) {
$.fn.scroll_lead = function (options) {
var defaults = {
speedup: 500
};
var options = $.extend({}, defaults, options);
return this.each(function () {
var $window_height = $(window).height();
var $document_height = $(document).height();
var $hide_lead;
$(window).scroll(function () {
var $scrollTop = $(window).scrollTop();
if (!$hide_lead) {
if ($scrollTop > ($document_height / 2)) {
$("#the_lead").slideDown(options.speedup);
} else {
$("#the_lead").slideUp(500, function () {
$(this).hide();
});
}
}
});
$('#hide_lead').click(function (e) {
$(this).parent().parents('div').hide();
hide_lead = true;
e.preventDefault();
});
});
};
})(jQuery);

Few things;
Assign $(this) to $this and use it inside any function inside your plugin, said in the tutorial, http://docs.jquery.com/Plugins/Authoring.
return this.each(function () {
var window_height = $(window).height();
var document_height = $(document).height();
var hide_lead;
$this = $(this);
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (!hide_lead) {
if (scrollTop > (document_height / 2)) {
$this.slideDown(options.speedup);
} else {
$this.slideUp(500, function () {
//$(this).hide();
});
}
}
});
$this.click(function (e) {
$(this).parent().parents('div').hide();
hide_lead = true;
e.preventDefault();
});
});
Try to avoid manipulation of parent objects inside your plugin, including $(window), and $(document).
It is ok to read properties of window and document, but if it is manipulated in your plugin, it will be maniuplated by number of times of your selector.
In your code, because you use this.each, you are binding scroll function of window several times. For example, $("div").scroll_lead() will bind 'scroll' method to window as many as tags of your document. The same applies to $(document) and all parent elements of plugin target.
If possible and it is your intention, use element scroll method, not window scroll.
To get scroll value $(el).scrollTop()
To scroll down, $(el).scrollTop(NUMBER)
To bind onScroll method, $(el).scroll( functtion() {...} )
Hope it helps

Firstly, your syntax is incorrect.
each would not exist on this as this is the context of the function, and not an element that jquery knows about.
try $(this).each which would get you closer.
remember Jquery.Each cannot iterrate over something that is not an object or array, so make sure that what you are trying to achieve makes sense.
What are you trying to achieve here?

Related

Resize one textarea with another?

I have a website with a few pages, each containing two textareas. All I'm trying to do is get it so that when the user resizes one of the textboxes, the other one sizes with it.
Here's what I've tried so far:
Attempt #1
$(document).ready(function(){
var taheight;
$('textarea').resize(function(){
taheight = $(this).height();
$('textarea').not(this).css('height',taheight);
});
});
I also tried .on('resize', function()... and some other variations before realising it couldn't be done that way on account of the fact the resize functionality on textareas is a browser control, rather than part of the DOM.
Then I found this jsFiddle: jsfiddle.net/gbouthenot/D2bZd/
I tried modifying it and came up with this:
$(document).ready(function(){
var textareaResize = function(source, dest) {
var resizeInt = null;
var thisTextArea;
var resizeEvent = function() {
dest.outerHeight(source.outerHeight());
};
source.on("mousedown", function(e) {
resizeInt = setInterval(resizeEvent, 1000/30);
thisTextArea = $(this).attr('id');
});
$(window).on("mouseup", function(e) {
if (resizeInt !== null) {
clearInterval(resizeInt);
}
resizeEvent();
});
};
textareaResize($("#" + thisTextArea), $("textarea"));
});
But that wouldn't get the id of the target textarea. I also tried thisTextArea = e.target.id, but that wouldn't work either.
Help! Where are am I going wrong?
You can do that by using jQuery UI resizable() and call the resize event from it.
$("textarea").resizable({
resize: function() {
//To get the id of the textarea being resized
var id = $(this).find('textarea').attr('id');
//You could also just put the resize function code here
}
});
jQuery UI Resizable

Jquery .change() event fires only once

So I'm fairly novice with jquery and js, so I apologise if this is a stupid error but after researching I can't figure it out.
So I have a list of data loaded initially in a template, one part of which is a dropdown box that lets you filter the data. My issue is that the filtering only works once? As in, the .change function inside $(document).ready() only fires the once.
There are two ways to reload the data, either click the logo and reload it all, or use the search bar. Doing either of these at any time also means the .change function never fires again. Not until you refresh the page.
var list_template, article_template, modal_template;
var current_article = list.heroes[0];
function showTemplate(template, data)
{
var html = template(data);
$("#content").html(html);
}
$(document).ready(function()
{
var source = $("#list-template").html();
list_template = Handlebars.compile(source);
source = $("#article-template").html();
article_template = Handlebars.compile(source);
source = $("#modal-template").html();
modal_template = Handlebars.compile(source);
showTemplate(list_template,list);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = list.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
$("#classFilter").change(function()
{
console.log("WOW!");
var classToFilter = this.value;
var filteredData =
{
heroes: list.heroes.filter(function(d)
{
if (d.heroClass.search(classToFilter) > -1)
{
return true;
}
return false;
})
};
console.log(filteredData);
showTemplate(list_template,filteredData);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = filteredData.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
});
$("#searchbox").keypress(function (e)
{
if(e.which == 13)
{
var rawSearchText = $('#searchbox').val();
var search_text = rawSearchText.toLowerCase();
var filteredData =
{
heroes: list.heroes.filter(function(d)
{
if (d.name.search(search_text) > -1)
{
return true;
}
return false;
})
};
console.log(filteredData);
showTemplate(list_template,filteredData);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = filteredData.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
}
});
$("#logo").click(function()
{
showTemplate(list_template,list);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = list.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
});
//$("#logo").click();
});
function displayModal(event)
{
var imageNumber = $(this).data("id");
console.log(imageNumber);
var html = modal_template(current_article.article[0].vicPose[imageNumber]);
$('#modal-container').html(html);
$("#imageModal").modal('show');
}
I should note two things: first, that the search bar works perfectly, and the anonymous function inside both of them is nearly identical, and like I said, the filtering works perfectly if you try it after the initial load. The second is that the same problem occurs replacing .change(anonymous function) with .on("change",anonymous function)
Any help or advice would be greatly appreciated. Thanks.
I agree with Fernando Urban's answer, but it doesn't actually explain what's going on.
You've created a handler attached to an HTML element (id="classFilter") which causes part of the HTML to be rewritten. I suspect that the handler overwrites the HTML which contains the element with the handler on it. So after this the user is clicking on a new HTML element, which looks like the old one but doesn't have a handler.
There are two ways round this. You could add code inside the handler which adds the handler to the new element which has just been created. In this case, that would mean making the handler a named function which refers to itself. Or (the easier way) you could do what Fernando did. If you do this, the event handler is attached to the body, but it only responds to clicks on the #classFilter element inside the body. In other words, when the user clicks anywhere on the body, jQuery checks whether the click happened on a body #classFilter element. This way, it doesn't matter whether the #classFilter existed when the handler was set. See "Direct and delegated events" in jQuery docs for .on method.
Try to use some reference like 'body' in the event listeners inside your DOM like:
$('body').on('click','.articleButton', function() {
//Do your stuff...
})
$('body').on('click','#classFilter', function() {
//Do your stuff...
})
$('body').on('keypress','#searchbox', function() {
//Do your stuff...
})
$('body').on('click','#logo', function() {
//Do your stuff...
})
This will work that you can fire it more than once.

jQuery window resize not working properly

Pseudocode:
If the window size equal to / or greater than 768 take content from the #sidebar and enter it in the place-holder div
If the window size is less than 768, do not run the above.
My jQuery code is as following:
$(document).ready(function () {
var $window = $(window);
function checkWidth() {
//set main vars
var windowsize = $window.width();
var $sideBarContent = $('#sidebar .dummy-content');
var $placeHolder = $('#place-holder');
//perform the main check
if (windowsize >= 768 && $('#place-holder').length) {
$($sideBarContent).clone(true)
.appendTo($($placeHolder));
}
else {
$($placeHolder).hide();
}
}
// Execute on load
checkWidth();
// Bind event listener
$(window).resize(checkWidth);
});
But the problem is that at the first page load everything is performed well, but at resize and back again, the script is not doing his job. Also on each resize all the content from the sidebar, is entered several times into the placeholder (instead of once).
And my working jsfiddle.
I just do not know what I'm doing wrong.
A few problems:
Your logic to add the new items is always true (as you only check whether the element exists - it always does).
Your child check needs to be inside the width check (separate check).
You need to show the placeHolder div again!
You have some redundant $() around jQuery variables
You have jQuery variables, for all the required elements, but sometimes you do not use them.
e.g.
$(document).ready(function () {
var $window = $(window);
function checkWidth() {
//set main vars
var windowsize = $window.width();
var $sideBarContent = $('#sidebar .dummy-content');
var $placeHolder = $('#place-holder');
//perform the main check
if (windowsize >= 768) {
if (!$placeHolder.children().length) {
$sideBarContent.clone(true)
.appendTo($placeHolder);
}
$placeHolder.show();
} else {
$placeHolder.hide();
}
}
// Bind event listener and do initial execute
$window.resize(checkWidth).trigger("resize");
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/jtfv4jjt/8/
Note: Your code can be simplified further as you only need to clone once at start and then just toggle visibility of that panel.
e.g.
$(document).ready(function () {
var $window = $(window);
var $sideBarContent = $('#sidebar .dummy-content');
var $placeHolder = $('#place-holder');
function checkWidth() {
$placeHolder.toggle($window.width() >= 768);
}
// Clone at start - then hide it
$sideBarContent.clone(true).appendTo($placeHolder);
// Bind event listener and do initial execute
$window.resize(checkWidth).trigger("resize");
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/jtfv4jjt/6/
Again this can be shortened:
$(function () {
var $window = $(window);
var $sideBarContent = $('#sidebar .dummy-content');
var $placeHolder = $('#place-holder');
// Clone content at start - then hide it
$sideBarContent.clone(true).appendTo($placeHolder);
// Bind event listener and do initial execute
$window.resize(function(){
$placeHolder.toggle($window.width() >= 768)
}).trigger("resize");
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/jtfv4jjt/7/
That's all for now :)
One last point:
If you use Twitter bootstrap, you can simply decorate the panel with an appropriate class (something like visible-md) and all this will happen for you :)
Take the event listener outside the document.ready() function:
$(document).ready(function(){
...
});
$(window).resize(checkWidth);
Also, take out the checkWidth() function outside the document.ready() function.

How to write a jquery plugin in OO way

i have written following jquery plugin. what i am trying to do is, when the user click on a link make the relevant div display: block base on the data attribute. But this plugin does not work. i have bn trying to figure this out for last two days. But i failed.
My HTML
<div class="container1">
asd
<div class="window1" data-window="a">
asd
</div>
</div>
<hr>
<div class="container2">
asdf1
asdf2
asdf3
<div class="window2" data-window="b">
asdf1
</div>
<div class="window2" data-window="c">
asdf2
</div>
<div class="window2" data-window="d">
asdf3
</div>
</div>
<script src="jquery-1.11.0.min.js"></script>
<script src="script.js"></script>
<script>
$('.container1').myPlugin({
link: $('.link1'),
container : $('.window1')
});
$('.container2').myPlugin({
link: $('.link2'),
container : $('.window2')
});
</script>
plugin
(function ($, window, document, undefind) {
MyPlugin = {
init : function (options, element) {
$.fn.myPlugin.config = $.extend({}, $.fn.myPlugin.config, options);
var link = $.fn.myPlugin.config.link;
link.on('click', this.secondFunc);
},
secondFunc : function () {
var dataLinkId = $(this).data('link'),
container = $($.fn.myPlugin.config).filter('[data-section="' + dataLinkId + '"]');
container.show();
}
};
$.fn.myPlugin = function(options) {
return this.each(function () {
var rezG = Object.create(MyPlugin);
rezG.init(options, this);
});
};
$.fn.myPlugin.config = {
link: $('.link'),
container : $('.container')
};
})(jQuery, window, document);
CSS
.window1, .window2 {
display: none;
}
DEMO
You need to use var to make sure your variables are all local and not global.
var MyPlugin = {
// ...
};
Also, in the init function, you are doing this:
$.fn.myPlugin.config = $.extend({}, $.fn.myPlugin.config, options);
This is overwriting $.fn.myPlugin.config which is the default options. This means that all elements that call myPlugin() will use the same config. You need to set the config on just the one instance.
this.config = $.extend({}, $.fn.myPlugin.config, options);
Your secondFunc doesn't have a reference to the object (rezG) instance, so it cannot access the config. You need to pass that to secondFunc(). One way is to use a closure to capture the instance.
secondFunc: function (rezG) {
return function(){
var dataLinkId = $(this).data('link'),
container = $(rezG.config.container).filter(function(){
return $(this).data('window') == dataLinkId;
});
container.show();
};
}
Then you bind it like so:
link.on('click', this.secondFunc(this));
Note that in secondFunc, you need to use config.container(not just config which is the object), and also your attribute is data-window, not data-section.
Updated demo: http://jsfiddle.net/K82gg/7/
Your plugin could be as simple as
(function ($, window, document, undefind) {
$.fn.myPlugin = function(options) {
// When $(stuff).myPlugin(...) is called
// this keyword inside of myPlugin function is referencing a set
// of elements plugin was called upon
// e.g. for call like $('.container1').myPlugin();
// this keyword will reference all elements selected by
// $('.container1') not jquery wrapped,
// in general it can be a any number.
return this.each(function pluginImplementation () {
// Here we iterate over the set, and for each element in the set
// we do some pretty standard click
var container = $(this);
// I use 'click.myPlugin' event instead just 'click' ale to later on
// do $(..).off('click.myPlugin') to remove only handlers that were
// attached by plugin (a good practice)
container.on('click.myPlugin', options.linkSelector, function(){
var dataLinkId = $(this).data('link');
container.find('[data-window="' + dataLinkId + '"]').toggle();
})
});
};
})(jQuery, window, document);
See the jsfiddle
However the code above may have a problem luginImplementation () function is created on each iteration and if the body of that function would be something more complicated it would be a mess. That is why it's better to create pluginImplementation () outside.
(function ($, window, document, undefind) {
// Notice that pluginImplementation () now accepts parameters
// They make it possible for pluginImplementation to know which
// elements it's working with
function pluginImplementation (container, options) {
container.on('click.myPlugin', options.linkSelector, function(){
var dataLinkId = $(this).data('link');
container.find('[data-window="' + dataLinkId + '"]').toggle();
})
}
$.fn.myPlugin = function(options) {
return this.each(function () {
pluginImplementation($(this), options);
});
};
})(jQuery, window, document);
The demo
That separation may be not good enough. You may want your plugin to be more OOP and what not. So you can go all OOPy like that:
(function ($, window, document, undefind) {
// For that purpose we create a class
// That describes behavior that our plugin provides
//
function MyPlugin(container, options) {
this.container = container;
this.options = options;
// To the topic of maintainability
// This could be parametrised as an option at plugin instantiation
this.eventName = 'click.myPlugin';
}
MyPlugin.prototype.attachClickHandlers = function() {
var self = this;
// This gets a little messy with all the thises vs selfs and a
// noname function wrapping the handler.
// The point is to preserve this keyword reference
// inside of clickHandler method.
// If I would have just self.clickHandler as a handler there
// this keyword inside of self.clickHandler would reference to
// whatever $(...).on binds handlers to i.e. triggering element.
// I need this keyword inside of self.clickHandler to point to
// "current" instance of MyPlugin, that's why I have wrapping
// function. It just lets me call clickHandler in the right context.
// clickHandler method also needs to know what link is being clicked
// so we pass that in as parameter.
self.container.on(self.eventName,
self.options.linkSelector,
function() {
self.clickHandler($(this));
})
}
MyPlugin.prototype.clickHandler = function(clickedLink) {
var dataLinkId = clickedLink.data('link');
this.container.find('[data-window="' + dataLinkId + '"]').toggle();
}
$.fn.myPlugin = function(options) {
return this.each(function () {
var pluginInstance = new MyPlugin($(this), options);
pluginInstance.attachClickHandlers();
});
};
})(jQuery, window, document);
In this implementation MyPlugin is a class (in javascript sense of the word class) which enables you to tackle each specific point in the way it behaves. and introduce all sorts of OOP features.
The demo

jQuery event to trigger action when a div is made visible

I'm using jQuery in my site and I would like to trigger certain actions when a certain div is made visible.
Is it possible to attach some sort of "isvisible" event handler to arbitrary divs and have certain code run when they the div is made visible?
I would like something like the following pseudocode:
$(function() {
$('#contentDiv').isvisible(function() {
alert("do something");
});
});
The alert("do something") code should not fire until the contentDiv is actually made visible.
Thanks.
You could always add to the original .show() method so you don't have to trigger events every time you show something or if you need it to work with legacy code:
Jquery extension:
jQuery(function($) {
var _oldShow = $.fn.show;
$.fn.show = function(speed, oldCallback) {
return $(this).each(function() {
var obj = $(this),
newCallback = function() {
if ($.isFunction(oldCallback)) {
oldCallback.apply(obj);
}
obj.trigger('afterShow');
};
// you can trigger a before show if you want
obj.trigger('beforeShow');
// now use the old function to show the element passing the new callback
_oldShow.apply(obj, [speed, newCallback]);
});
}
});
Usage example:
jQuery(function($) {
$('#test')
.bind('beforeShow', function() {
alert('beforeShow');
})
.bind('afterShow', function() {
alert('afterShow');
})
.show(1000, function() {
alert('in show callback');
})
.show();
});
This effectively lets you do something beforeShow and afterShow while still executing the normal behavior of the original .show() method.
You could also create another method so you don't have to override the original .show() method.
The problem is being addressed by DOM mutation observers. They allow you to bind an observer (a function) to events of changing content, text or attributes of dom elements.
With the release of IE11, all major browsers support this feature, check http://caniuse.com/mutationobserver
The example code is a follows:
$(function() {
$('#show').click(function() {
$('#testdiv').show();
});
var observer = new MutationObserver(function(mutations) {
alert('Attributes changed!');
});
var target = document.querySelector('#testdiv');
observer.observe(target, {
attributes: true
});
});
<div id="testdiv" style="display:none;">hidden</div>
<button id="show">Show hidden div</button>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
There is no native event you can hook into for this however you can trigger an event from your script after you have made the div visible using the .trigger function
e.g
//declare event to run when div is visible
function isVisible(){
//do something
}
//hookup the event
$('#someDivId').bind('isVisible', isVisible);
//show div and trigger custom event in callback when div is visible
$('#someDivId').show('slow', function(){
$(this).trigger('isVisible');
});
You can use jQuery's Live Query plugin.
And write code as follows:
$('#contentDiv:visible').livequery(function() {
alert("do something");
});
Then everytime the contentDiv is visible, "do something" will be alerted!
redsquare's solution is the right answer.
But as an IN-THEORY solution you can write a function which is selecting the elements classed by .visibilityCheck (not all visible elements) and check their visibility property value; if true then do something.
Afterward, the function should be performed periodically using the setInterval() function. You can stop the timer using the clearInterval() upon successful call-out.
Here's an example:
function foo() {
$('.visibilityCheck').each(function() {
if ($(this).is(':visible')){
// do something
}
});
}
window.setInterval(foo, 100);
You can also perform some performance improvements on it, however, the solution is basically absurd to be used in action. So...
The following code (pulled from http://maximeparmentier.com/2012/11/06/bind-show-hide-events-with-jquery/) will enable you to use $('#someDiv').on('show', someFunc);.
(function ($) {
$.each(['show', 'hide'], function (i, ev) {
var el = $.fn[ev];
$.fn[ev] = function () {
this.trigger(ev);
return el.apply(this, arguments);
};
});
})(jQuery);
If you want to trigger the event on all elements (and child elements) that are actually made visible, by $.show, toggle, toggleClass, addClass, or removeClass:
$.each(["show", "toggle", "toggleClass", "addClass", "removeClass"], function(){
var _oldFn = $.fn[this];
$.fn[this] = function(){
var hidden = this.find(":hidden").add(this.filter(":hidden"));
var result = _oldFn.apply(this, arguments);
hidden.filter(":visible").each(function(){
$(this).triggerHandler("show"); //No bubbling
});
return result;
}
});
And now your element:
$("#myLazyUl").bind("show", function(){
alert(this);
});
You could add overrides to additional jQuery functions by adding them to the array at the top (like "attr")
a hide/show event trigger based on Glenns ideea:
removed toggle because it fires show/hide and we don't want 2fires for one event
$(function(){
$.each(["show","hide", "toggleClass", "addClass", "removeClass"], function(){
var _oldFn = $.fn[this];
$.fn[this] = function(){
var hidden = this.find(":hidden").add(this.filter(":hidden"));
var visible = this.find(":visible").add(this.filter(":visible"));
var result = _oldFn.apply(this, arguments);
hidden.filter(":visible").each(function(){
$(this).triggerHandler("show");
});
visible.filter(":hidden").each(function(){
$(this).triggerHandler("hide");
});
return result;
}
});
});
I had this same problem and created a jQuery plugin to solve it for our site.
https://github.com/shaunbowe/jquery.visibilityChanged
Here is how you would use it based on your example:
$('#contentDiv').visibilityChanged(function(element, visible) {
alert("do something");
});
What helped me here is recent ResizeObserver spec polyfill:
const divEl = $('#section60');
const ro = new ResizeObserver(() => {
if (divEl.is(':visible')) {
console.log("it's visible now!");
}
});
ro.observe(divEl[0]);
Note that it's crossbrowser and performant (no polling).
Just bind a trigger with the selector and put the code into the trigger event:
jQuery(function() {
jQuery("#contentDiv:hidden").show().trigger('show');
jQuery('#contentDiv').on('show', function() {
console.log('#contentDiv is now visible');
// your code here
});
});
Use jQuery Waypoints :
$('#contentDiv').waypoint(function() {
alert('do something');
});
Other examples on the site of jQuery Waypoints.
I did a simple setinterval function to achieve this. If element with class div1 is visible, it sets div2 to be visible. I know not a good method, but a simple fix.
setInterval(function(){
if($('.div1').is(':visible')){
$('.div2').show();
}
else {
$('.div2').hide();
}
}, 100);
You can also try jQuery appear plugin as mentioned in parallel thread https://stackoverflow.com/a/3535028/741782
This support easing and trigger event after animation done! [tested on jQuery 2.2.4]
(function ($) {
$.each(['show', 'hide', 'fadeOut', 'fadeIn'], function (i, ev) {
var el = $.fn[ev];
$.fn[ev] = function () {
var result = el.apply(this, arguments);
var _self=this;
result.promise().done(function () {
_self.triggerHandler(ev, [result]);
//console.log(_self);
});
return result;
};
});
})(jQuery);
Inspired By http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/
There is a jQuery plugin available for watching change in DOM attributes,
https://github.com/darcyclarke/jQuery-Watch-Plugin
The plugin wraps All you need do is bind MutationObserver
You can then use it to watch the div using:
$("#selector").watch('css', function() {
console.log("Visibility: " + this.style.display == 'none'?'hidden':'shown'));
//or any random events
});
Hope this will do the job in simplest manner:
$("#myID").on('show').trigger('displayShow');
$('#myID').off('displayShow').on('displayShow', function(e) {
console.log('This event will be triggered when myID will be visible');
});
I changed the hide/show event trigger from Catalint based on Glenns idea.
My problem was that I have a modular application. I change between modules showing and hiding divs parents. Then when I hide a module and show another one, with his method I have a visible delay when I change between modules. I only need sometimes to liten this event, and in some special childs. So I decided to notify only the childs with the class "displayObserver"
$.each(["show", "hide", "toggleClass", "addClass", "removeClass"], function () {
var _oldFn = $.fn[this];
$.fn[this] = function () {
var hidden = this.find(".displayObserver:hidden").add(this.filter(":hidden"));
var visible = this.find(".displayObserver:visible").add(this.filter(":visible"));
var result = _oldFn.apply(this, arguments);
hidden.filter(":visible").each(function () {
$(this).triggerHandler("show");
});
visible.filter(":hidden").each(function () {
$(this).triggerHandler("hide");
});
return result;
}
});
Then when a child wants to listen for "show" or "hide" event I have to add him the class "displayObserver", and when It does not want to continue listen it, I remove him the class
bindDisplayEvent: function () {
$("#child1").addClass("displayObserver");
$("#child1").off("show", this.onParentShow);
$("#child1").on("show", this.onParentShow);
},
bindDisplayEvent: function () {
$("#child1").removeClass("displayObserver");
$("#child1").off("show", this.onParentShow);
},
I wish help
One way to do this.
Works only on visibility changes that are made by css class change, but can be extended to watch for attribute changes too.
var observer = new MutationObserver(function(mutations) {
var clone = $(mutations[0].target).clone();
clone.removeClass();
for(var i = 0; i < mutations.length; i++){
clone.addClass(mutations[i].oldValue);
}
$(document.body).append(clone);
var cloneVisibility = $(clone).is(":visible");
$(clone).remove();
if (cloneVisibility != $(mutations[0].target).is(":visible")){
var visibilityChangedEvent = document.createEvent('Event');
visibilityChangedEvent.initEvent('visibilityChanged', true, true);
mutations[0].target.dispatchEvent(visibilityChangedEvent);
}
});
var targets = $('.ui-collapsible-content');
$.each(targets, function(i,target){
target.addEventListener('visibilityChanged',VisbilityChanedEventHandler});
target.addEventListener('DOMNodeRemovedFromDocument',VisbilityChanedEventHandler });
observer.observe(target, { attributes: true, attributeFilter : ['class'], childList: false, attributeOldValue: true });
});
function VisbilityChanedEventHandler(e){console.log('Kaboom babe'); console.log(e.target); }
my solution:
; (function ($) {
$.each([ "toggle", "show", "hide" ], function( i, name ) {
var cssFn = $.fn[ name ];
$.fn[ name ] = function( speed, easing, callback ) {
if(speed == null || typeof speed === "boolean"){
var ret=cssFn.apply( this, arguments )
$.fn.triggerVisibleEvent.apply(this,arguments)
return ret
}else{
var that=this
var new_callback=function(){
callback.call(this)
$.fn.triggerVisibleEvent.apply(that,arguments)
}
var ret=this.animate( genFx( name, true ), speed, easing, new_callback )
return ret
}
};
});
$.fn.triggerVisibleEvent=function(){
this.each(function(){
if($(this).is(':visible')){
$(this).trigger('visible')
$(this).find('[data-trigger-visible-event]').triggerVisibleEvent()
}
})
}
})(jQuery);
example usage:
if(!$info_center.is(':visible')){
$info_center.attr('data-trigger-visible-event','true').one('visible',processMoreLessButton)
}else{
processMoreLessButton()
}
function processMoreLessButton(){
//some logic
}
$( window ).scroll(function(e,i) {
win_top = $( window ).scrollTop();
win_bottom = $( window ).height() + win_top;
//console.log( win_top,win_bottom );
$('.onvisible').each(function()
{
t = $(this).offset().top;
b = t + $(this).height();
if( t > win_top && b < win_bottom )
alert("do something");
});
});
$(function() {
$(document).click(function (){
if ($('#contentDiv').is(':visible')) {
alert("Visible");
} else {
alert("Hidden");
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="contentDiv">Test I'm here</div>
<button onclick="$('#contentDiv').toggle();">Toggle the div</button>
<div id="welcometo">Özhan</div>
<input type="button" name="ooo"
onclick="JavaScript:
if(document.all.welcometo.style.display=='none') {
document.all.welcometo.style.display='';
} else {
document.all.welcometo.style.display='none';
}">
This code auto control not required query visible or unvisible control

Categories

Resources