jQuery add close buttons dynamically on elements - javascript

I'm trying adding fancy close buttons dynamically to html elements
code is:
function add_close_box(element,img_close_box,base_url_close_box){
var i;
$.each($('.'+element),function(i){
$(this).addClass('close_box'+i);
var x = $('.close_box'+i).offset().left;
var y = $('.close_box'+i).offset().top;
$('body').append('<img src="'+base_url_close_box+img_close_box+'">');
$('.img_close_box_'+i).css({'position':'absolute','top':y-10,'left':x+$('.close_box'+i).width()-20});
$('.img_close_box_'+i).live('click',function(){
var hei = $('.'+element).height();
$('.img_close_box_'+i+','+'.close_box'+i).hide();
$.each('.'+element,function(e){
$('.img_close_box_'+(e)).animate({
'top':'-='+hei
},0);
});
});
});
}
it works good but when click on button and element is fadeOut() other close buttons doesn't follows their own related element cause of absolute position they remain in same positions while elements scroll up (first element fadeOut next element scroll up and close button remain in same position)
i would like that close buttons could follow their related element.
Also cause my lazy mind i would like to know if anyone knows some good jquery plugin to do what I'm trying to code, something with also ajax callback after closebox button click (do not reply to watch jQuery UI please :) )
thanks ;)

I have to make a few assumptions because you haven't posted your markup. I think Diodeus is on the right track. You're using absolute positioning for your new close button. Absolute positioning will look up in the DOM until it finds a relatively positioned element to anchor the coordinates. if the element you pass in doesn't have the property position:relative then the button could be positioned anywhere.
Two possible fixes:
Add relative positioning to everything you add the close button to:
element.css({'position':'relative'});
Obviously this will replace any other previously set positioning. There's a chance this blows up your layout if you're adding close buttons all over the place.
The second option is to make sure the only relatively positioned element is something high in the DOM, so all of your close buttons are positioned based on the same thing. It's a little hacky, but then you can position them based on the x and y of the element:
var x = element.position().x;
var y = element.position().y;
// you can add the necessary offsets like element width or height

Related

Event listener does not work when image element blocks it [duplicate]

I have a div that has background:transparent, along with border. Underneath this div, I have more elements.
Currently, I'm able to click the underlying elements when I click outside of the overlay div. However, I'm unable to click the underlying elements when clicking directly on the overlay div.
I want to be able to click through this div so that I can click on the underlying elements.
Yes, you CAN do this.
Using pointer-events: none along with CSS conditional statements for IE11 (does not work in IE10 or below), you can get a cross browser compatible solution for this problem.
Using AlphaImageLoader, you can even put transparent .PNG/.GIFs in the overlay div and have clicks flow through to elements underneath.
CSS:
pointer-events: none;
background: url('your_transparent.png');
IE11 conditional:
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='your_transparent.png', sizingMethod='scale');
background: none !important;
Here is a basic example page with all the code.
Yes, you CAN force overlapping layers to pass through (ignore) click events.
PLUS you CAN have specific children excluded from this behavior...
You can do this, using pointer-events
pointer-events influences the reaction to click-, tap-, scroll- und hover events.
In a layer that should ignore / pass-through mentioned events you set
pointer-events: none;
Children of that unresponsive layer that need to react mouse / tap events again need:
pointer-events: auto;
That second part is very helpful if you work with multiple overlapping div layers (probably some parents being transparent), where you need to be able to click on child elements and only that child elements.
Example usage:
.parent {
pointer-events:none;
}
.child {
pointer-events:auto;
}
<div class="parent">
I'm unresponsive
I'm clickable again, wohoo !
</div>
Allowing the user to click through a div to the underlying element depends on the browser. All modern browsers, including Firefox, Chrome, Safari, and Opera, understand pointer-events:none.
For IE, it depends on the background. If the background is transparent, clickthrough works without you needing to do anything. On the other hand, for something like background:white; opacity:0; filter:Alpha(opacity=0);, IE needs manual event forwarding.
See a JSFiddle test and CanIUse pointer events.
I'm adding this answer because I didn’t see it here in full. I was able to do this using elementFromPoint. So basically:
attach a click to the div you want to be clicked through
hide it
determine what element the pointer is on
fire the click on the element there.
var range-selector= $("")
.css("position", "absolute").addClass("range-selector")
.appendTo("")
.click(function(e) {
_range-selector.hide();
$(document.elementFromPoint(e.clientX,e.clientY)).trigger("click");
});
In my case the overlaying div is absolutely positioned—I am not sure if this makes a difference. This works on IE8/9, Safari Chrome and Firefox at least.
Hide overlaying the element
Determine cursor coordinates
Get element on those coordinates
Trigger click on element
Show overlaying element again
$('#elementontop').click(e => {
$('#elementontop').hide();
$(document.elementFromPoint(e.clientX, e.clientY)).trigger("click");
$('#elementontop').show();
});
I needed to do this and decided to take this route:
$('.overlay').click(function(e){
var left = $(window).scrollLeft();
var top = $(window).scrollTop();
//hide the overlay for now so the document can find the underlying elements
$(this).css('display','none');
//use the current scroll position to deduct from the click position
$(document.elementFromPoint(e.pageX-left, e.pageY-top)).click();
//show the overlay again
$(this).css('display','block');
});
I currently work with canvas speech balloons. But because the balloon with the pointer is wrapped in a div, some links under it aren't click able anymore. I cant use extjs in this case.
See basic example for my speech balloon tutorial requires HTML5
So I decided to collect all link coordinates from inside the balloons in an array.
var clickarray=[];
function getcoo(thatdiv){
thatdiv.find(".link").each(function(){
var offset=$(this).offset();
clickarray.unshift([(offset.left),
(offset.top),
(offset.left+$(this).width()),
(offset.top+$(this).height()),
($(this).attr('name')),
1]);
});
}
I call this function on each (new) balloon. It grabs the coordinates of the left/top and right/down corners of a link.class - additionally the name attribute for what to do if someone clicks in that coordinates and I loved to set a 1 which means that it wasn't clicked jet. And unshift this array to the clickarray. You could use push too.
To work with that array:
$("body").click(function(event){
event.preventDefault();//if it is a a-tag
var x=event.pageX;
var y=event.pageY;
var job="";
for(var i in clickarray){
if(x>=clickarray[i][0] && x<=clickarray[i][2] && y>=clickarray[i][1] && y<=clickarray[i][3] && clickarray[i][5]==1){
job=clickarray[i][4];
clickarray[i][5]=0;//set to allready clicked
break;
}
}
if(job.length>0){
// --do some thing with the job --
}
});
This function proofs the coordinates of a body click event or whether it was already clicked and returns the name attribute. I think it is not necessary to go deeper, but you see it is not that complicate.
Hope in was enlish...
Another idea to try (situationally) would be to:
Put the content you want in a div;
Put the non-clicking overlay over the entire page with a z-index higher,
make another cropped copy of the original div
overlay and abs position the copy div in the same place as the original content you want to be clickable with an even higher z-index?
Any thoughts?
I think the event.stopPropagation(); should be mentioned here as well. Add this to the Click function of your button.
Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
Just wrap a tag around all the HTML extract, for example
<a href="/categories/1">
<img alt="test1" class="img-responsive" src="/assets/photo.jpg" />
<div class="caption bg-orange">
<h2>
test1
</h2>
</div>
</a>
in my example my caption class has hover effects, that with pointer-events:none; you just will lose
wrapping the content will keep your hover effects and you can click in all the picture, div included, regards!
An easier way would be to inline the transparent background image using Data URIs as follows:
.click-through {
pointer-events: none;
background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
}
I think that you can consider changing your markup. If I am not wrong, you'd like to put an invisible layer above the document and your invisible markup may be preceding your document image (is this correct?).
Instead, I propose that you put the invisible right after the document image but changing the position to absolute.
Notice that you need a parent element to have position: relative and then you will be able to use this idea. Otherwise your absolute layer will be placed just in the top left corner.
An absolute position element is positioned relative to the first parent
element that has a position other than static.
If no such element is found, the containing block is html
Hope this helps. See here for more information about CSS positioning.
You can place an AP overlay like...
#overlay {
position: absolute;
top: -79px;
left: -60px;
height: 80px;
width: 380px;
z-index: 2;
background: url(fake.gif);
}
<div id="overlay"></div>
just put it over where you dont want ie cliked. Works in all.
This is not a precise answer for the question but may help in finding a workaround for it.
I had an image I was hiding on page load and displaying when waiting on an AJAX call then hiding again however...
I found the only way to display my image when loading the page then make it disappear and be able to click things where the image was located before hiding it was to put the image into a DIV, make the size of the DIV 10x10 pixels or small enough to prevent it causing an issue then hiding the containing div. This allowed the image to overflow the div while visible and when the div was hidden, only the divs area was affected by inability to click objects beneath and not the whole size of the image the DIV contained and was displaying.
I tried all the methods to hide the image including CSS display=none/block, opacity=0, hiding the image with hidden=true. All of them resulted in my image being hidden but the area where it was displayed to act like there was a cover over the stuff underneath so clicks and so on wouldn't act on the underlying objects. Once the image was inside a tiny DIV and I hid the tiny DIV, the entire area occupied by the image was clear and only the tiny area under the DIV I hid was affected but as I made it small enough (10x10 pixels), the issue was fixed (sort of).
I found this to be a dirty workaround for what should be a simple issue but I was not able to find any way to hide the object in its native format without a container. My object was in the form of etc. If anyone has a better way, please let me know.
I couldn't always use pointer-events: none in my scenario, because I wanted both the overlay and the underlying element(s) to be clickable / selectable.
The DOM structure looked like this:
<div id="outerElement">
<div id="canvas-wrapper">
<canvas id="overlay"></canvas>
</div>
<!-- Omitted: element(s) behind canvas that should still be selectable -->
</div>
(The outerElement, canvas-wrapper and canvas elements have the same size.)
To make the elements behind the canvas act normally (e.g. selectable, editable), I used the following code:
canvasWrapper.style.pointerEvents = 'none';
outerElement.addEventListener('mousedown', event => {
const clickedOnElementInCanvas = yourCheck // TODO: check if the event *would* click a canvas element.
if (!clickedOnElementInCanvas) {
// if necessary, add logic to deselect your canvas elements ...
wrapper.style.pointerEvents = 'none';
return true;
}
// Check if we emitted the event ourselves (avoid endless loop)
if (event.isTrusted) {
// Manually forward element to the canvas
const mouseEvent = new MouseEvent(event.type, event);
canvas.dispatchEvent(mouseEvent);
mouseEvent.stopPropagation();
}
return true;
});
Some canvas objects also came with input fields, so I had to allow keyboard events, too.
To do this, I had to update the pointerEvents property based on whether a canvas input field was currently focused or not:
onCanvasModified(canvas, () => {
const inputFieldInCanvasActive = // TODO: Check if an input field of the canvas is active.
wrapper.style.pointerEvents = inputFieldInCanvasActive ? 'auto' : 'none';
});
it doesn't work that way. the work around is to manually check the coordinates of the mouse click against the area occupied by each element.
area occupied by an element can found found by 1. getting the location of the element with respect to the top left of the page, and 2. the width and the height. a library like jQuery makes this pretty simple, although it can be done in plain js. adding an event handler for mousemove on the document object will provide continuous updates of the mouse position from the top and left of the page. deciding if the mouse is over any given object consists of checking if the mouse position is between the left, right, top and bottom edges of an element.
Nope, you can't click ‘through’ an element. You can get the co-ordinates of the click and try to work out what element was underneath the clicked element, but this is really tedious for browsers that don't have document.elementFromPoint. Then you still have to emulate the default action of clicking, which isn't necessarily trivial depending on what elements you have under there.
Since you've got a fully-transparent window area, you'll probably be better off implementing it as separate border elements around the outside, leaving the centre area free of obstruction so you can really just click straight through.

How to know if my element is overflowed

I have a slider that contains N elements. Each element will by translated by N pixels when the user click on the next button. When the element is out of the wrapper div, it disappears because it is overflowed by another element.
My plugin does not use any margins, just the transform property.
I would like to know if there is a way to know if my element is out of the div. :visible does not work for my problem because the element is already visible but overflowed.
If I understand correctly, one way to do it would be to compare the position of this element to the size (width/height or both) of his parent.
With Jquery you could do it this way:
<script>
//This is the position of the right side of the element
//relative to his parent
var rightPos = $("#element").position().left + $("#element").width();
//And bottom side
var botPos = $("#element").position().top + $("#element").height();
if (rightPos > $("#element").parent().width()) {
//The element is outside the right limit of the the parent block
} else if (botPos > $("#element").parent.height()) {
//It's outside the bottom limit of the parent block
}
</script>
If it's not the parent you could then adapt this code to compare the position to the width of the correct div, preferably by using the jquery offset() method instead of position().
By determine parent width and get child width then use if condition
if($('span').width() > divWidth){
alert('Overflowed!');
// do something!
}
jsFiddle Demo
if you update your question with your html then I can update with your codes.
You could give the wrapper div the CSS property of overflow: hidden
This would mean that any elements inside of it are not visible when they leave the bounds of the wrapper.
Otherwise you could check whether your element is outside of the wrapper div using jQuery to compare the position to that of the parent.
There is a nice tool for testing if an element is visible on the screen.
Detect if a DOM Element is Truly Visible
It looks at an object and checks each of its parents to see if it’s still visible to the user.

Angular js do not scroll when list element height change

I have a list of elements. Each element has an expanded and collapsed state.
When user expands one of the element, all other elements need to collapse. The list is inside a div which could be scrolled to allow seeing the entire list.
element 1
element 2
element 3
element 4
element 5
Lets say, element 1 is in expanded state. When I expand element 3, I collapse element 1. The problem is that when I do that, element 3 scrolls up. I want to avoid this and position element 3 to same position.
This is a pseudo code. There could be some minor syntax errors, but do ignore them, since my actual code has lot more going on and I don't want to paste the entire thing here.
The controller will include expand function like:
public expandElement(currentElement: any, previousElement: any) {
// get the height of previous element
var scrollOffset: number = angular.element("#" + this.getAnchorId(previousElement).offsetHeight;
// this function will collapse the element decreasing the height of it
previousElement.collapse();
// scroll to the newly expanded element
this.$timeout(() => {
var anchorId: string = this.getAnchorId(element);
var element = angular.element("#" + anchorId)[0];
var offset = element.getBoundingClientRect().top + scrollOffset;
this.$anchorScroll.yOffset = offset;
this.$location.hash(anchorId);
this.$anchorScroll();
});
previousElement = currentElement;
}
The html will look something like:
<div ng-repeat="element in listElements"
id="{{getAnchorId(element)}}">
<my-directive ng-click="expandElement(element, previousElement)"></my-directive>
</div>
This currently is not working at all, but even if I get it to work, there would be a small UI jump that could happen. I want to avoid scroll completely.
Is there another easy way to just block the scroll when angular expands an element?
Appreciate the help.
Thanks!
I got your problem, but i have one question. Consider you have more elements in the list and you will expand say 50th element then it has to scroll the main container to show the content of that expanded panel, i think it is the desired functionality in the scroll. I know i am not answering your question but, just thought of reconsidering it.
Probably you have to save scroll position and after click function got triggered change the scroll position back to saved state. There could be flick effect. If you want to hide the flick effect, hide the div show with a slow animation. Hope it helps.

jQuery scrolling out of view issue

I am working on building a schedule. So far it's pretty straight-forward. There is one bit of functionality I am having issues with.
I have an ul that has a fixed height to allow scrolling. There are "Labels" (li.dayLabel) withing the ul that separate the hours. What I am trying to do is to have it so that when a label is scrolled out of view it will change the text in the placeholder to it's text. Then once that works, I need it to work in reverse. So when they label scrolls back into view it updates the placeholder. Basically, I am trying to make the placeholder be a title for the available items until another label is then scrolled out of view. This is for scrolling from the top. So as you scroll down the list the placeholder is meant to be a title for the section you are viewing until you reach another section and it takes its place. Then when you scroll back down I need it to replace the text with the previous li.dayLabel so the sections stay organized. I hope this makes sense.
You can see what I am trying to do by looking at the original that I am basing this off of. Notice how the placeholder changes as you scroll down the list and changes back when you scroll back up.
Demo: jsFiddle // Note: line 54 is the part that is in question
I originally used:
$(".snlf-schedule-list li.dayLabel:visible:first").text();
as the :first selector is suppose to only match a single element.
I later tried:
$(".snlf-schedule-list li.dayLabel:visible").filter(":eq(0)")
as this is suppose to be the same thing.
It seems that when an element is out of view it still is considered :visible I believe this is my issue.
Am I doing this completely wrong? I was under the impression that when you scroll an element like this it should no longer be :visible. After reading the documentation I have learned that this is not the correct selector to use.
It would appear that scrollTop is how I should be doing this. Now I have used scrollTop for scrolling down pages to disable animations when not in view but I am not clear on how to untilize this for a ul with scrollbars.
I tried:
var _first = $('li.dayLabel:first'); // next element to scroll out of view?
if( $(this).scrollTop() > (_first.offset().top+_first.height())) {
// Out of view
console.log("out");
} else {
// in view
console.log("in");
}
Updated Demo: jsFiddle
But it seems to be redundant as it's already calculating the first element so I am not sure how to get the correct element (the next one that's about to scroll out of view.) Then I need this to work when they scroll back up...
Any insight on this is much appreciated. Hopefully it's something simple I am just over complicating or missing completely.
Thanks,
Jeremy
The solution for my case was:
// Set placeholder text on scroll
var _scrollCagePositionTop = $(".snlf-schedule-list").offset().top;
var _first = $('li.dayLabel:first'); // first dayLabel element
$(".snlf-schedule-list").scroll(function(){
var _lastOffText = $(_first).text();
$("li.dayLabel").each(function() {
if ($(this).offset().top < _scrollCagePositionTop) {
_lastOffText = $(this).text();
}
});
$("#schedule-placeholder").text(_lastOffText);
});
What I did was set the known position of the top of the scroll cage (_scrollCagePositionTop)
When the user scrolls I set a variable _lastOffText that keeps track of the last item text content when scrolled out of view (less offset top than the palceholder). I then set this value to the placeholder.
This method allows me to have the proper text in my placeholder when the user scrolls up or down.
To fix the issue of an empty placeholder when the user scrolls back to the top I just set the default of _lastOffText to be the text of the first label ($(_first).text())
Hope others find this useful.
Here is the working demo: jsFiddle Final

JScrollPane Dynamic Height

The demo page referenced can be found here.
I'm trying to determine a way that on the click of a parent category (ex: Stone Tiles, Stone Sinks), that the JScrollPane would re-determine the current height and adjust as needed. Unfortunately, my attempts to do so have not worked yet.
I referenced the example here which provided the following function (to do a refresh)...
api.reinitialise();
I've tried to setup this function to be triggered by the category parents like so...
var pane = $('.menuwrap')
pane.jScrollPane();
var api = pane.data('jsp');
var i = 1;
$("li.expandable.parent").click(function() {
api.reinitialise();
});
Unfortunately, while I was able to verify the click is being rendered, the function (api.reinitialize) doesn't appear to be working. I'm hoping that a fresh pair of eyes could point me in the right direction. :-)
Thanks!
The problem is that api.reinitialise executes immediately after the click, and the li element will not have expanded yet so when jscroll pane goes to to recalculate the height it gets it wrong. You can try adding a delay but the best solution would be to bind api.reinitialise() to an event that's triggered once the your list has finished expanding. I'm not sure how you're expanding the div within the li but if for instance it's using .animate, you could bind the api.reinitialise to the animation complete event.
Also noted that not all the parent li's have the class parent associated it to them. I would expect you would want the pane to reinitialize on the expansion and collapsing of all the main li elements.
Hope that helps !
Cheers :)
What you can do is have your inner divs expanded by default, and then close them with jquery, rather than in the CSS directly.
So instead of doing this:
.mydiv.closed {display:none}
do this in your jquery after the elements are drawn to the page:
$('.mydiv.closed').hide();
This will load the jscrollpane at the necessary height, and then collapse what you want to be initially hidden.

Categories