AlpineJs triggering a click.outside event when adding modal - javascript

I have a modal that's is loaded with alpines when a button is click, the thing is it also triggers a click.outside event. Is there a way of stopping this happening? I have tried finding a solution online but can't find a thing, could someone help a newbie out?
<div class="mt-12" x-data="{isTrailerModalVisible: false,}">
<button
x-on:click="isTrailerModalVisible = true";
class="flex bg-blue-500 text-white font-semibold px-4 py-4 hover:bg-blue-600 rounded transition ease-in-out duration-150 items-center"
>
<i class="far fa-play-circle"></i>
<div class="ml-2">Play Trailer</div>
</button>
<template x-if="isTrailerModalVisible">
<div
x-show="isTrailerModalVisible"
style="background-color: rgba(0, 0, 0, .5);"
class="z-50 fixed top-0 left-0 w-full h-full flex items-center shadow-lg overflow-y-auto"
>
<div class="container mx-auto lg:px-32 rounded-lg overflow-y-auto">
<div class="bg-gray-300 rounded"
>
<div class="flex justify-end pr-4 pt-2">
<button
#click="isTrailerModalVisible = false"
#keydown.escape.window="isTrailerModalVisible = false"
class="text-3xl leading-none hover:text-gray-300"
>
×
</button>
</div>
<!--Finish the clicked away for closing the modal-->
<div class="modal-body px-8 py-8" #click.outside="console.log('clicked')";>
<div class="responsive-container overflow-hidden relative" style="padding-top: 56.25%" >
<iframe width="360" height="315" class="responsive-iframe absolute top-0 left-0 w-full h-full" src="{{ $game['trailer'] }}" style="border:0;" allow="autoplay; encrypted-media" allowfullscreen></iframe>
</div>
</div>
</div>
</div>
</div>
</template>
</div>

I ran into this problem as well when updating from Alpine 2 to 3. Switching #click.outside to #mousedown.outside solved it for me.
I'm not sure if this is a bug in Alpine since #click.away (the old way to call #click.outside) worked in Alpine 2 but it does work with this solution.

Related

How to dim a component when a modal is pop out (Tailwind Flowbite)?

I want to ask. I have a layout, there is a button when i click the button, a pop up will appear, the problem is when the modal appears, there are some components that are not dimmed, such as the button and the navbar as shown in the picture, I use vue cli and tailwind and for the modal I use flowbite. I've been looking for a way but still can't find it. Can anyone help me?
My website currently has a mobile-based display
this the web without modal
and this is the web when the button is clicked
here is the code in navbarWhite.vue
<template>
<header class="sticky h-14 top-0 z-50 bg-white" :class="boxShadow">
<div class="flex flex-row">
<div class="absolute" v-if="srcPictureLeft">
<img
#click="onClickBack"
class="ml-7 py-4 cursor-pointer text-black"
:src="require(`../assets/icon/${srcPictureLeft}`)"
/>
</div>
<div v-else></div>
<div class="py-4 relative mx-auto font-semibold text-black text-xl">
{{ title }}
</div>
</div>
</header>
</template>
<style>
</style>
<script>
export default {
name: "NavbarWhite",
props: {
onClickBack: {
type: Function,
},
title: String,
srcPictureLeft: String,
boxShadow: String,
},
};
</script>
and this is the button component (ButtonBottom.vue)
<template>
<div
class="
sticky
w-full
absolute
mb-0
bottom-0
z-50
bg-white
h-16
drop-shadow-[0_0_4px_rgba(0,0,0,0.25)]
"
>
<div class="mx-[30px] py-2">
<button-primary
class="
bg-green-button
text-white
hover:bg-green-button-darker hover:rounded-[32px]
"
>
<slot>Button</slot>
</button-primary>
</div>
</div>
</template>
<script>
import ButtonPrimary from "#/components/ButtonPrimary.vue";
export default {
name: "ButtonBottom",
components: {
ButtonPrimary,
},
};
</script>
and this is the parent where all the component called
<template>
<div class="h-screen relative">
<div class="h-[94%]">
<navbar-white
boxShadow="shadow-[0_0_10px_0_rgba(0,0,0,0.25)]"
srcPictureLeft="backIconBlack.svg"
:onClickBack="goBack"
title="Ringkasan Transaksi"
/>
<div class="mt-10 mx-[30px]">
<div class="flex flex-row justify-between mb-7">
<div class="font-semibold">No. Rekening</div>
<div>12345678</div>
</div>
<div class="flex flex-row justify-between mb-7">
<div class="font-semibold">Nama Penerima</div>
<div>Lorem Ipsum</div>
</div>
<div class="flex flex-row justify-between mb-4">
<div class="font-semibold">Bank Tujuan</div>
<div>12345678</div>
</div>
<hr class="mb-4" />
<div class="flex flex-row justify-between mb-7">
<div class="font-semibold">Nominal</div>
<div>Rp 200.000</div>
</div>
<div class="flex flex-row justify-between mb-4">
<div class="font-semibold">Fee</div>
<div>Rp 2.500</div>
</div>
<div class="flex flex-row justify-between">
<div class="font-semibold">Total</div>
<div class="font-semibold">Rp 202.500</div>
</div>
</div>
</div>
<button-bottom data-modal-toggle="biayaAdmin">Selanjutnya</button-bottom>
<!-- modal start here -->
<div
id="biayaAdmin"
tabindex="-1"
class="
hidden
overflow-y-auto overflow-x-hidden
fixed
top-0
right-0
left-0
z-50
w-full
md:inset-0
h-modal
md:h-full
"
>
<div class="relative p-4 w-full max-w-md h-full md:h-auto">
<!-- Modal content -->
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
<!-- Modal header -->
<div
class="
flex
justify-between
items-center
p-5
rounded-t
border-b
dark:border-gray-600
"
>
<h3 class="text-xl font-medium text-gray-900 dark:text-white">
Small modal
</h3>
<button
type="button"
class="
text-gray-400
bg-transparent
hover:bg-gray-200 hover:text-gray-900
rounded-lg
text-sm
p-1.5
ml-auto
inline-flex
items-center
dark:hover:bg-gray-600 dark:hover:text-white
"
data-modal-toggle="biayaAdmin"
>
<svg
aria-hidden="true"
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<!-- Modal body -->
<div class="p-6 space-y-6">
<p
class="text-base leading-relaxed text-gray-500 dark:text-gray-400"
>
With less than a month to go before the European Union enacts new
consumer privacy laws for its citizens, companies around the world
are updating their terms of service agreements to comply.
</p>
<p
class="text-base leading-relaxed text-gray-500 dark:text-gray-400"
>
The European Union’s General Data Protection Regulation (G.D.P.R.)
goes into effect on May 25 and is meant to ensure a common set of
data rights in the European Union. It requires organizations to
notify users as soon as possible of high-risk data breaches that
could personally affect them.
</p>
</div>
<!-- Modal footer -->
<div
class="
flex
items-center
p-6
space-x-2
rounded-b
border-t border-gray-200
dark:border-gray-600
"
>
<button
data-modal-toggle="biayaAdmin"
type="button"
class="
text-white
bg-blue-700
hover:bg-blue-800
focus:ring-4 focus:outline-none focus:ring-blue-300
font-medium
rounded-lg
text-sm
px-5
py-2.5
text-center
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
"
>
I accept
</button>
<button
data-modal-toggle="biayaAdmin"
type="button"
class="
text-gray-500
bg-white
hover:bg-gray-100
focus:ring-4 focus:outline-none focus:ring-gray-200
rounded-lg
border border-gray-200
text-sm
font-medium
px-5
py-2.5
hover:text-gray-900
focus:z-10
dark:bg-gray-700
dark:text-gray-300
dark:border-gray-500
dark:hover:text-white
dark:hover:bg-gray-600
dark:focus:ring-gray-600
"
>
Decline
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ButtonBottom from "#/components/ButtonBottom.vue";
import NavbarWhite from "#/components/NavbarWhite.vue";
export default {
name: "TransactionSummary",
components: {
ButtonBottom,
NavbarWhite,
},
methods: {
goBack() {
this.$router.go(-1);
},
},
};
</script>
The css z-index of your header and footer might be higher than the z-index of the overlay mask.
Try inspecting the elements and evaluate their z-index values, try forcing new values to ensure that the z-index of the header and footer is a lower value than the grey overlay mask.
The issue might be the lack of a z-index value on some of those elements to indicate which should appear over the top of another.
https://www.w3schools.com/cssref/pr_pos_z-index.asp

Need help in converting Jquery Ajax Modal to Alpine JS

I am attempting to convert a Bootstrap Jquery Modal with an ajax request into a Tailwind Css Alpine JS modal with Ajax request.
Jquery Code
$(document).on('click', 'a[data-ajax-popup="true"], button[data-ajax-popup="true"], div[data-ajax-popup="true"]', function () {
var title = $(this).data('title');
var size = ($(this).data('size') == '') ? 'md' : $(this).data('size');
var url = $(this).data('url');
$("#commonModal .modal-title").html(title);
$("#commonModal .modal-dialog").addClass('modal-' + size);
$.ajax({
url: url,
success: function (data) {
$('#commonModal .modal-body').html(data);
$("#commonModal").modal('show');
daterange_set();
taskCheckbox();
common_bind("#commonModal");
commonLoader();
},
error: function (data) {
data = data.responseJSON;
show_toastr('Error', data.error, 'error')
}
});
});
The Jquery Modal Template located in the layouts.app
<div class="modal fade" id="commonModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div>
<h4 class="h4 font-weight-400 float-left modal-title" id="exampleModalLabel"></h4>
{{__('Close')}}
</div>
<div class="modal-body">
</div>
</div>
</div>
</div>
The code to call the modal from a different blade view i.e. names.index.blade.php
<div class="col-xl-2 col-lg-2 col-md-4 col-sm-6 col-6">
<a href="#" data-url="{{ route('names.create') }}"
data-ajax-popup="true"
data-title="{{__('Create Name')}}"
class="btn btn-xs btn-white btn-icon-only width-auto">
<i class="fas fa-plus"></i> {{__('Create')}}
</a>
</div>
This is doing an ajax call to the NamesController which contains the code
public function create()
{
if(Auth::user()->can('create name'))
{
return view('names.create');
}
else
{
return response()->json(['error' => __('Permission Denied.')], 401);
}
}
I have been able to code the AlpineJs modal. However unable to wrap my head around calling the ajax functionality.
The modal code with Alpine and Tailwind
<div x-data="{ 'showModal': false }" x-cloak #keydown.escape="showModal = false">
<!-- Trigger for Modal -->
<button type="button" #click="showModal = true">Open Modal</button>
<div x-show="showModal" class="fixed inset-0 z-30 flex items-center justify-center overflow-auto bg-black bg-opacity-50">
<!-- begin::Modal content -->
<div class="sm:h-[calc(100%-5rem)] max-w-4xl my-6 mx-auto relative w-auto pointer-events-none">
<div #click.away="showModal = false"
x-transition:enter="motion-safe:ease-out duration-300"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
class="max-h-full overflow-hidden relative flex flex-col w-full pointer-events-auto
bg-white bg-clip-padding rounded-md
border-none shadow-lg">
<!-- begin::Modal Header -->
<div class="flex flex-shrink-0 items-center justify-between p-4
bg-blue-200
border-b border-gray-200 rounded-t-md">
<h3 class="font-semibold text-base sm:text-xl
text-gray-50">
Modal title
</h3>
<button type="button"
#click="showModal = false"
class="text-gray-200 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-toggle="default-modal">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
<!-- end::Modal Header -->
<!-- begin::Modal Body -->
<div class="flex-auto overflow-y-auto relative p-4
dark:bg-modal-body-dark">
<p>This is some placeholder content.</p>
</div>
<!-- end::Modal Body -->
<!-- begin::Modal Footer -->
<div
class="flex flex-shrink-0 flex-wrap space-x-2 items-center justify-end p-4
bg-gray-200
border-t border-gray-200 rounded-b-md">
<button data-modal-toggle="default-modal" type="button"
class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:ring-gray-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600">
Cancel
</button>
<button data-modal-toggle="default-modal" type="button"
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
I accept
</button>
</div>
<!-- end::Modal Footer -->
</div>
</div>
<!-- end::Modal content -->
</div>
</div>
How do I get the same Jquery functionality with the AlpineJs modal template located in the layouts.app and doing an ajax call from names.index.blade.php

How can I use javascript to hide by default the navigation links in my navbar on small screens?

I set up my navber using tailwind css and was able to make it responsive. When viewed on a smaller screen the navigation links are displayed underneath each other in a flex block kind of way. However when viewed in a mobile viewport the navigation links are showing by default. Implimented a button that can be clicked to show and hide it but how can I use javascript to actually write the code for that to work? Here is a snippet of my code:
<script src="https://cdn.tailwindcss.com"></script>
<!--Navbar-->
<nav id="navbar" class="fixed inset-x-0 flex flex-center justify-between flex-wrap p-5 lg:mx-10 lg:mt-5 shadow-xl rounded-lg">
<div class="flex items-center flex-shrink-0 text-green-600 mr-6">
<img class="fill-current h-8 w-8 mr-2" src="images/Logo.svg" alt="Logo" width="20">
<span class="font-bold text-2xl tracking-tight">First Last</span>
</div>
<div class="block md:hidden">
<button class="flex items-center px-3 py-2 border rounded hover:text-green-300 hover:border-green-300">
<svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
</button>
</div>
<div class="w-full block md:flex md:items-center md:w-auto">
<div class="text-sm md:flex-grow">
About
Skills
Projects
Experience
</div>
</div>
</nav>
Image of Navbar seen from the mobile view
Change block md:flex to hidden md:flex which will hide the menu on mobile screens.
You can then use Element.classList.toggle to toggle the hidden class when the button is clicked on mobile screens.
Try this
document.addEventListener('DOMContentLoaded', function(){
let menu = document.getElementById('navbar-menu');
document.getElementById('navbar-toggler').addEventListener('click', event => {
menu.classList.toggle('hidden')
})
})
<script src="https://cdn.tailwindcss.com"></script>
<!--Navbar-->
<nav id="navbar" class="fixed inset-x-0 flex flex-center justify-between flex-wrap p-5 lg:mx-10 lg:mt-5 shadow-xl rounded-lg">
<div class="flex items-center flex-shrink-0 text-green-600 mr-6">
<img class="fill-current h-8 w-8 mr-2" src="images/Logo.svg" alt="Logo" width="20">
<span class="font-bold text-2xl tracking-tight">First Last</span>
</div>
<div class="block md:hidden">
<button class="flex items-center px-3 py-2 border rounded hover:text-green-300 hover:border-green-300" id="navbar-toggler">
<svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
</button>
</div>
<div class="w-full hidden md:flex md:items-center md:w-auto" id="navbar-menu">
<div class="text-sm md:flex-grow">
About
Skills
Projects
Experience
</div>
</div>
</nav>
You can use Javascript to select the element by its id and add or remove classes/styles to achieve the toggle effect.
ex:
const navbar = document.querySelector("#navbar");
if (navbar.classList.contains("something"){
someelement.style.display = "none";
anotherElement.style.display = "block";
navbar.classList.add("someClass");
}
Here is an example codepen for the same.
https://codepen.io/ljc-dev/pen/MWoRayy

AlpineJs, adding dynamic id to element and comparing on mouseover event

I want to display some data beloning to an image when a user hovers over it, how would I go adding and comparing id's from a livewire component ?
Livewire view
<div x-data="{tooltip : false}" class="grid grid-cols-3 gap-4 ml-2 antialiased text-gray-900">
<div #mouseover.away="{tooltip = false}">
#forelse($animals as $animal)
<img id="{{$animal->animals->id}}" x-on:mouseover="{tooltip = !tooltip}"
src="https://source.unsplash.com/random/350x350"
alt="Animal image"
class="object-cover rounded-lg shadow-md ">
<div x-show.transition="tooltip" class="absolute -mt-16 ">
<div class="p-6 bg-white rounded-lg shadow-lg">
<h4 class="mt-1 text-xl font-semibold leading-tight uppercase truncate">This is
: {{$animal->animals->name}}</h4>
<div class="mt-4">
<span class="font-semibold text-teal-600 text-md">4/5 ratings </span>
<span class="text-sm text-gray-600">(based on 234 ratings)</span>
</div>
</div>
</div>
#empty
<p class="text-center">No animals found</p>
#endforelse
</div>
</div>
Livewire class
class Dashboard extends Component
{
public function render()
{
$animal = UserAnimal::with('animals')->where('user_id', '=', Auth::id())->get();
return view('livewire.dashboard')->with('animals', $animal);
}
}
This snippet works when only one image is displayed, but as soon as another image is added the tooltip stops working,
E.g the user hovers over the first image and the name of that animal is displayed in the hovering tooltip.
Instead of having a global tooltip attribute on the parent div, I would use a tooltip attribute for each animal - which means you probably need to adjust your loop and html structure.
<div class="grid grid-cols-3 gap-4 ml-2 antialiased text-gray-900">
<div x-data="{tooltip : false}">
<img id="{{$animal->animals->id}}" x-on:mouseover="{tooltip = !tooltip}" x-on:mouseout="{tooltip = !tooltip}" src="https://source.unsplash.com/random/350x350" alt="Animal image" class="object-cover rounded-lg shadow-md ">
<div x-show.transition="tooltip" class="absolute -mt-16 ">
<div class="p-6 bg-white rounded-lg shadow-lg">
<h4 class="mt-1 text-xl font-semibold leading-tight uppercase truncate">This is
: A dog</h4>
<div class="mt-4">
<span class="font-semibold text-teal-600 text-md">4/5 ratings </span>
<span class="text-sm text-gray-600">(based on 234 ratings)</span>
</div>
</div>
</div>
</div>
<div x-data="{tooltip : false}">
<img id="{{$animal->animals->id}}" x-on:mouseover="{tooltip = !tooltip}" x-on:mouseout="{tooltip = !tooltip}" src="https://source.unsplash.com/random/350x350" alt="Animal image" class="object-cover rounded-lg shadow-md ">
<div x-show.transition="tooltip" class="absolute -mt-16 ">
<div class="p-6 bg-white rounded-lg shadow-lg">
<h4 class="mt-1 text-xl font-semibold leading-tight uppercase truncate">This is
: A cat</h4>
<div class="mt-4">
<span class="font-semibold text-teal-600 text-md">1/5 ratings </span>
<span class="text-sm text-gray-600">(based on 2 ratings)</span>
</div>
</div>
</div>
</div>
</div>
Example in Codepen

Dropup in TailwindCSS / AlpineJS

Does anyone know how to build a 'dropup' in TailwindCSS /AlpineJS? I know how to build a dropdown but can't manage to make a dropup.
My dropdown:
<link href="https://unpkg.com/tailwindcss#^1.0/dist/tailwind.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta charset="utf-8" />
<div class="flex-shrink-0 flex border-t border-gray-200 p-4">
<a href="#" class="flex-shrink-0 group block">
<div class="flex items-center">
<div #click.away="open = false" class="relative" x-data="{ open: false }">
<div>
<button #click="open = !open" class="max-w-xs flex items-center text-sm rounded-full text-white focus:outline-none focus:shadow-solid transition ease-in-out duration-150">
<img class="inline-block h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
<div class="ml-3">
<p class="text-sm leading-5 font-medium text-gray-700 group-hover:text-gray-900">
John Doe
</p>
<p class="text-xs text-left leading-4 font-medium text-gray-500 group-hover:text-gray-700 transition ease-in-out duration-150">
View profile
</p>
</div>
</button>
</div>
<div x-show="open" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" class="origin-top-right absolute left-0 mt-2 -mr-1 w-48 rounded-md shadow-lg">
<div class="py-1 rounded-md bg-white shadow-xs relative">
Your Profile
Settings
Sign out
</div>
</div>
</div>
</div>
</a>
</div>
https://jsfiddle.net/s4m6vea7/
Thanks!
With minimal changes
I do know how to build this. Building the drop up is easy. Spacing it out evenly is more difficult. In this code pen, I took your example and made it work without changing very much. I added the bottom-0 class to the dropdown. I also added an mb-12 class to it would sit above the button. This isn't a super dynamic fix though, you'll have to add margins to the bottom of every element you use it on.
A solid solution
I changed this code pen to have a wrapper around the dropup. By wrapping the dropup I was able to get the bottom-0 class to start at the top of the dropdown. Any margin added will be consistent regardless of the dropup height.

Categories