Trigger event for an svg element that moves behind another one? - javascript

the desired output is for example: When the red masked rectangle is moving behind one of the "holes", the hole needs to change the fill (or some other attr).
Is it possible to add some sort of event that triggers when the moving_rect is entering/leaving the area of the hole element?
in action:
https://jsfiddle.net/qwertasyx/n9v7L95j/
also here as snippet:
var draw = SVG('myDrawing').size('100%',200);
var rect_group = draw.group()
//we dont want to see this
var background = rect_group.rect(200,150).center(250,100).fill('#000')
// we want too see through those 'holes'
var hole1 = rect_group.rect(40,40).center(290,70 ).fill('#fff')
var hole2 = rect_group.rect(40,40).center(290,130).fill('#fff')
var hole3 = rect_group.rect(40,40).center(220,70 ).fill('#fff')
var hole4 = rect_group.rect(40,40).center(220,130).fill('#fff')
// object that we see through the holes
var moving_rect = draw.rect(40,40).fill('red').center(250,100)
//mask obj
var mask = draw.mask()
$('#mask').on('click',function(e){
mask.add(rect_group)
moving_rect.maskWith(mask)
})
$('#animate').on('click',function(e){ moving_rect.animate(250).move(160,40).animate(250).move(250,40).animate(250).move(300,80).animate(250).move(250,160).animate(250).center(250,100)
})
svg{
border-style: solid;
border-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.5/svg.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!--
Bootstrap docs: https://getbootstrap.com/docs
-->
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h5>Example svg -> click button for animation </h5><button id="mask" class="btn btn-primary">mask rect</button> <button id="animate" class="btn btn-primary">animate</button>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div id="myDrawing"></div>
</div>
</div>
</div>
<hr>

So I crafted together a solution where on every animation step it is checked if one of the rectangles (holes) is intersecting with the moving rectangle.
If an intersection is detected, it changes the fill color of the "holes".
// animate()...
.duringAll(function () {
// get box of the rectangle
var bbox = this.bbox()
// intersection function. You should define it somewhere outside
// instead of redefining every time
var intersect = function(boxA, boxB) {
return (boxA.x < boxB.x2 && boxA.x2 > boxB.x &&
boxA.y < boxB.y2 && boxA.y2 > boxB.y)
}
// go trough the boxes of every hole defined before
boxes.forEach(function (el, index) {
if(intersect(bbox, el)) {
// change color of fill on intersection
rects[index].fill('#555')
} else {
rects[index].fill('#fff')
}
})
})
Working fiddle:
https://jsfiddle.net/Fuzzy/n9v7L95j/5/
It would be trivial to fire an event instead. You can just go:
this.fire('intersection', {index: index})
// then bind with
moving_rect.on('intersection', function (ev) {
var index = ev.detail.index
// do somthing
rects[index].fill('#333')
})

Related

js asynchronous problem just work first neighbor

I want click neighbor and change the main country. I click first card ok no problem. but other cards not working. This is my code:
function renderNeighbors(data) {
let html = "";
for (let country of data) {
html += `
<div class="col-2 mt-2">
<div class="card click-change">
<img src="${country.flags.png}" class="card-img-top">
<div class="card-body">
<h6 class="card-title neighborName">${country.name.common}</h6>
</div>
</div>
</div>
`;
}
document.querySelector("#neighbors").innerHTML = html;
document.querySelector(".click-change").addEventListener("click", () => {
let neighborName = document.querySelector(".neighborName").innerHTML;
getCountry(neighborName);
})
}
in photos I point red circle in my browser. that's only working card.
enter image description here
I want to click neighbors and change main country. but only work for first neighbor
here is my all html code https://pastebin.com/Sb9XWZhy
here, the problem you are setting your event only for the first chosen element that matches your locator .
querySelector return only the first element that matches.
you should add the event for all of your nodes that match the locator.
It's supposed that querySelectorAll returns an array that you can loop over it like that.
for (let element of document.querySelectorAll(".click-change")){
element.addEventListener("click", () => {
let neighborName =
element.children[1].children[0].innerHTML;//here I'm accessing the neighbourName from the element itself, I'm not sure about the indices but you can handle them yourself
getCountry(neighborName);
})
}

mouseover function on 3 elements only functions on the last element

I am a newbie in javascript, so be aware. I already searched on this website for a solution of this issue but I was not able to find the working solution.
The problem is on my development website: https://famnabuurs.nl (it is on the homepage). I use Wordpress with DIVI and now some javascript.
The three columns I created have a similar setup. The columns all have an id so I can manage the behaviour. When you hover over a column some changes should be initiated: background-color of the column should switch to black, stripe background should change from blue to black and the image in the background should grow. That all works for for an individual column. But now I have 3 columns it only works at the last one. What I saw form other issues on this website it will have to do with the definition of my variables, but no idea where to update my script.
My html looks like this, the same for every column except for the id:
<div id='research'>
<div class='textblock'>
.. my text ...
</div>
<div class='myimage koek-achtergrond'>
.. my background image ...
</div>
<div class='myimage koek-stripe'>
.. my other image ...
</div>
</div>
Here is my generic code:
<script>
function mouseover() {
console.log('mouse over this column: ',this.id);
var column = document.getElementById(this.id);
column.style.backgroundColor = 'black';
var stripe = document.getElementById(this.id).getElementsByClassName('koek-stripe')[0];
stripe.classList.add('koek-stripe-hovered');
var background = document.getElementById(this.id).getElementsByClassName('koek-achtergrond')[0];
background.classList.add('koek-transform');
}
function mouseleave() {
console.log('mouse leaves this column: ',this.id);
var column = document.getElementById(this.id);
column.style.backgroundColor = 'var(--primary-blue-color)';
var stripe = document.getElementById(this.id).getElementsByClassName('koek-stripe')[0];
stripe.classList.remove('koek-stripe-hovered');
var background = document.getElementById(this.id).getElementsByClassName('koek-achtergrond')[0];
background.classList.remove('koek-transform');
console.log('classList after leaving: ',background.classList);
}
</script>
In every column I add a short script, where only the columname is changed, in line with the name in the html:
<script>
var columnname = 'research';
columnElement = document.getElementById(columnname);
columnElement.onmouseover = function() {
columnElement.addEventListener('mouseover', mouseover);
}
columnElement.onmouseleave = function() {
columnElement.addEventListener('mouseleave', mouseleave);
}
console.log('kolomdetails: ',columnElement);
</script>
In my opinion it is not relevant to show the css in this case, since the issue refers to the javascript. The problem is that the classes that should be added in the generic script only are applied in the last column. The question is: why do they not remain in de first two columns?
I saw some remarks in other threads indicating it is the classical error in Javascript. So I hope this can help me understand variables in a better way.
Consider using event delegation. Here's a minimal reproducable example to work with:
[`mouseover`, `mouseout`].forEach(h => document.addEventListener(h, handle));
function handle(evt) {
const el2Handle = evt.target.closest(
[`.textblock`, `.koek-achtergrond`, `.koek-stripe`]
.find(c => evt.target.closest(c)) );
if (el2Handle) {
el2Handle.style.color = evt.type === `mouseover` ? `red` : ``;
}
}
#research div {
cursor: pointer;
}
<div id='research'>
<div class='textblock'>
.. my text ...
</div>
<div class='myimage koek-achtergrond'>
.. my background image ...
</div>
<div class='myimage koek-stripe'>
.. my other image ...
</div>
</div>
Yeesssss, this works fine. I took the answer as pointed out below and tailored it to my final solution:
<script>
document.addEventListener(`mouseover`, handle);
document.addEventListener(`mouseout`, handle);
function handle(evt) {
const origin = evt.target;
if ([`.koek-textblock`, `.koek-achtergrond`, `.koek-stripe`].find( c => origin.closest('.koek-kolom'))) {
kolom = origin.closest('.koek-kolom');
kolom.style.backgroundColor = evt.type === `mouseover` ? 'black' : ``;
stripe = kolom.getElementsByClassName('koek-stripe')[0];
stripe.style.backgroundImage = evt.type === `mouseover` ? "url('https://famnabuurs.nl/wp-content/uploads/2022/10/stripe_zwart.png')": ``;
backgroundimage = kolom.getElementsByClassName('koek-achtergrond')[0];
backgroundimage.style.transform = evt.type === `mouseover` ? "scale(1.2) translate(-80px, 40px)" : ``;
}
}
</script>
I also added the class 'koek-kolom' to the column so I am sure I find the right parent.
If you want you can see the result on https://famnabuurs.nl/afbeelding-hoveren-improved/

Show only 10 items using jquery infinite scroll within the div

Hi I found lots of examples related to this question, but so far the examples that I see they don't match my needs.On my div at the moment I load all the list content retrieved from my function,My goal is I want to be able to only show 6 items and keep on appending the other 6 until the list is exhausted using the infinite scroll in jQuery.
This is how my list look within the div.
<div class="listOfAnything">
<div class="all">apple</div>
<div class="all">Banana</div>
<div class="all">Guava</div>
<div class="all">Pear</div>
<div class="all">mango</div>
<div class="all">Grapes</div>
<div class="all">Avocado</div>
<div class="all">Orange</div>
<div class="all">Lemon</div>
<div class="all">Nartjie</div>
<div class="all">Granadilla</div>
<div class="all">pawpaw</div>
<div class="all">Ginger</div>
<div class="all">Watermelon</div>
<div class="all">potato</div>
<div class="all">Sweet Potato</div>
<div class="all">Peach</div>
</div>
I've tried to follow the tutorials on http://scrollmagic.io/examples/advanced/infinite_scrolling.html but I had no luck because i got stuck here
function addBoxes (amount) {
for (i=1; i<=amount; i++) {
var randomColor = '#'+('00000'+ (Math.random()*0xFFFFFF<<0).toString(16)).slice(-6);
$("<div></div>")
.addClass("box1")
.css("background-color", randomColor)
.appendTo(".dynamicContent #content");
}
// "loading" done -> revert to normal state
scene.update(); // make sure the scene gets the new start position
$("#loader").removeClass("active");
}
// add some boxes to start with.
addBoxes(18);
Because I already have the content on my div.
Added the scroll function
function addBoxes (amount) {
for (i=1; i<=amount; i++) {
var randomColor = '#'+('00000'+ (Math.random()*0xFFFFFF<<0).toString(16)).slice(-6);
$("<div></div>")
.addClass("box1")
.css("background-color", randomColor)
.appendTo(".dynamicContent #content");
}
// "loading" done -> revert to normal state
scene.update(); // make sure the scene gets the new start position
$("#loader").removeClass("active");
}
// add some boxes to start with.
addBoxes(6);
// do things on mousescroll
$(window).bind('mousewheel DOMMouseScroll', function(event)
{
if (event.originalEvent.wheelDelta < 0 || event.originalEvent.detail > 0) {
setTimeout(function(){
addBoxes(6);
}, 1000);
}
});

Forcing two row-fluid to be same size

I have been struggling with a problem and can't figure out how to solve it:
I have made a FIDDLE :
HTML:
<div class="row-fluid">
<div class="span6">
<div class="alert alert-info">
this has text <br>
this has text <br>
</div>
</div>
<div class="span6">
<div class="alert alert-success">
<div class="media">
This needs to be same size as the other box.<br />
This needs to be same size as the other box.<br />
This needs to be same size as the other box.<br />
This needs to be same size as the other box.<br />
This needs to be same size as the other box.<br />
This needs to be same size as the other box.
</div>
</div>
</div>
</div>
</div>
</body>
I want both boxes to be the same size regardless if theres text in the boxes or not. I have tried adding some javascript, but I havent figured out how to do it.
So here is an example using jQuery, it looks for all your rows then targets the columns that must match sizes inside each row. My having the row and col passed in as parameters it should mean if you change your structure you can just update the call with what ever class names you are using.
var updateHeights = function (rowTarget, colTarget) {
$(rowTarget).each(function () {
var maxHeight = 0;
$(colTarget, this).each(function () {
if (maxHeight <$(this).height()) {
maxHeight = $(this).height();
}
});
$(colTarget, this).each(function () {
$(this).height(maxHeight);
});
});
};
updateHeights('.row-fluid','.alert');
fiddle: http://jsfiddle.net/leighking2/rk4t6c45/
The one thing i don;t like about it is the fact it loops twice, once to find the largest height then again to set it.
Simply add min-height you want to have
FIDDLE
.alert{
min-height:150px;
}
Hope i have understood you right
You need to add the "alert" class together with the span-6 class, because the span-6 class is the one giving the visual information after that you can set min-height for alert
Here's a pure JS solution for equalising the heights of matched elements. It also works on resize.
function setHeights(){
//get all the elements that need to be equal height:
var elements = document.getElementsByClassName('alert');
//call the equaliseHeights prototype method
elements.equaliseHeights();
}
/* Extend the HTMLCollection prototype */
HTMLCollection.prototype.equaliseHeights = function() {
var maxHeight=0;
//loop through the collection to find the height of the tallest element:
for (var i = 0; i < this.length; i++) {
this[i].style.minHeight = 0; //reset min-height
var thisHeight = this[i].offsetHeight; //measure height
maxHeight = (thisHeight>maxHeight)?thisHeight:maxHeight; //store the greatest height
}
//now set the min-height for all:
for (var i = 0; i < this.length; i++) {
this[i].style.minHeight = maxHeight+"px"; //set min-height
}
return this;
};
/* on load */
(function waitForReady(){
if (document.readyState === "complete") {
setHeights();//on load, call method
window.onresize = setHeights;//on resize, call method
} else {
setTimeout(waitForReady,10);
}
})();
http://jsfiddle.net/r5ye65vz/5/

Making an element draggable without jquery ui?

I'm trying to build an image cropper similar to Twitters - http://jsfiddle.net/yarKr/1/. What I'm stuck on is the ability to drag the image. What is the best way to do this without resorting to jquery ui?
<div class="canvas">
<span class="window"></span>
<img src="http://www.dailystab.com/blog/wp-content/uploads/2009/03/katy-perry-esquire-4.jpg" class="draggable" />
</div>
I want to be able to move drag the image around inside the .canvas div.
Something like this will work: jsFiddle
var TheDraggable = null;
$(document).ready(function () {
$('.draggable').on({
mousedown: function () { TheDraggable = $(this); },
mouseup: function () { TheDraggable = null; }
});
$(document).mousemove(function (e) {
if (TheDraggable) {
TheDraggable.css({'top': e.pageY, 'left': e.pageX});
}
});
});
And then for the CSS you add this: .draggable { position:absolute; }
You could rewrite this and add some form of easing on the repositioning, change the cursor or add a more precise starting point based on where the initial click happened on the picture but overall, that should get you started.
How about, while dragging, making the position absolute and setting it to or near the position of the mouse?
This is mine.
http://jsfiddle.net/pd1vojsL/
3 draggable buttons in a div, dragging constrained by div.
<div id="parent" class="parent">
<button id="button1" class="button">Drag me</button>
<button id="button2" class="button">Drag me</button>
<button id="button3" class="button">Drag me</button>
</div>
<div id="log1"></div>
<div id="log2"></div>
Requires JQuery (only):
$(function() {
$('.button').mousedown(function(e) {
if(e.which===1) {
var button = $(this);
var parent_height = button.parent().innerHeight();
var top = parseInt(button.css('top')); //current top position
var original_ypos = button.css('top','').position().top; //original ypos (without top)
button.css({top:top+'px'}); //restore top pos
var drag_min_ypos = 0-original_ypos;
var drag_max_ypos = parent_height-original_ypos-button.outerHeight();
var drag_start_ypos = e.clientY;
$('#log1').text('mousedown top: '+top+', original_ypos: '+original_ypos);
$(window).on('mousemove',function(e) {
//Drag started
button.addClass('drag');
var new_top = top+(e.clientY-drag_start_ypos);
button.css({top:new_top+'px'});
if(new_top<drag_min_ypos) { button.css({top:drag_min_ypos+'px'}); }
if(new_top>drag_max_ypos) { button.css({top:drag_max_ypos+'px'}); }
$('#log2').text('mousemove min: '+drag_min_ypos+', max: '+drag_max_ypos+', new_top: '+new_top);
//Outdated code below (reason: drag contrains too early)
/*if(new_top>=drag_min_ypos&&new_top<=drag_max_ypos) {
button.css({top:new_top+'px'});
}*/
});
$(window).on('mouseup',function(e) {
if(e.which===1) {
//Drag finished
$('.button').removeClass('drag');
$(window).off('mouseup mousemove');
$('#log1').text('mouseup');
$('#log2').text('');
}
});
}
});
});
Drag icon - less jumping
Borrowing from frenchie's answer I have to create a movable popup on the fly. Have to add dragability to the object after it's created. This not flawless - any faster than a slow drag and the cursor leaves the behind until you let go, at which point the <div> then snaps and sticks to the cursor movements until you click again.
<!-- language: lang-html -->
<input type="button" value="pop" onclick="pop('NN')" id="btnNN"><!-- NN is the query rownumber -->
<b style="display:none;" id="protoContent">
<div id="divdrag~">
<img style="float:left;" id="drag~">
my form <input id="inp1_~">...
</div>
</b>
<!-- language: lang-js -->
var TheDraggable = null;
$(document).ready(function () {
$(document).mousemove(function (e) {
if (TheDraggable) {
TheDraggable.css({'top': e.pageY-15, 'left': e.pageX-15});
}//"-15" gets the cursor back on img inside the <div>
});
});
var gOpenPop="";
function pop(NN){
if(gOpenPop!=""){
document.getElementById("divdrag"+gOpenPop).style.display="none";//hide opened
if(gOpenPop==NN){
gOpenPop="";
return;
}
}
gOpenPop=NN;
//add div after the button
$("#btn"+NN).after(
//get div w/ form, replace of any "~"s with argument NN
$("#protoContent").html().replace(/~/g,NN)
);
//ojb created, now bind click for dragability to the img
$('#drag'+NN).on(
{mousedown: function () {TheDraggable = $("#divdrag"+NN); },//
mouseup: function () { TheDraggable = null; }
});
...
}

Categories