Thank you! Your feedback has been delivered
Thank you! Your feedback has been sent

AngularJS Drag and drop into iframe

Looking to implement the example found here: http://stackoverflow.com/questions/21067030/drag-drop-across-iframe-using-angular-directive/22160213

Direct link to example -

http://78.110.163.229/angDnd/outer.html

In either what they used, or using AngularJS Drag & Drop...

But with 2 changes:

1) Fix cursor position: The example in the stack overflow thread has a bug with the cursor position, where when you drag from parent into iframe it thinks you're trying to drop on the element about 50px below wherever your cursor is.

2) Ensure it still works after CSS Transform Scale: Try using CSS Transform to scale the iframe down to let's say 75% and ensure the drag & drop still works with accurate cursor positioning.

User Gravatar

kevinsproles

Posted Mar 4 2014 9:36 UTC

$200


  • Assigned To NBoychev
  • Solved
  • javascript
    angular
    angular dragdrop
  • 6992 Views

8 Replies


Hi kevinsporales,

Here is my solution:

The html for both pages is modified, so please update on your side.

Regards,

Nikola Boychev

User Gravatar

NBoychev

Posted Mar 11 2014 15:32 UTC

Any chance you can demonstrate this on a codepen or jsfiddle (or similar)?

User Gravatar

kevinsproles

Posted Mar 11 2014 16:53 UTC

Hi Kevin,

It's hard to reproduce this on jsfiddle or codepen, because of the iframe. I've uploaded this to my webpage: http://nboychev.com/tests/angular/Drag%20Drop%20iFrame%20using%20Angular%20JS%20Directive.html Tell me when you check it so I can remove it.

Regards,

Nikola Boychev

User Gravatar

NBoychev

Posted Mar 11 2014 17:04 UTC

Hi Nikola,

Thanks for your help! Unfortunately your solution has not addressed the cursor position completely. Tested in latest Chrome. The problem is when you try to drag & drop to nest an element inside another. To reproduce: drag a few elements into the iframe, then drag an element over the text "Droppable on iFrame" (plus or minus 200px) and notice that the elements in the iframe have the dotted red line to indicate a drop zone target. Now you can see the entire alignment is off by a few hundred pixels to the cursor position.

User Gravatar

kevinsproles

Posted Mar 11 2014 23:39 UTC

Hi Kevin,

The code is updated now. It will need just a little bit more tune for the scale fix:

http://nboychev.com/tests/angular/Drag%20Drop%20iFrame%20using%20Angular%20JS%20Directive.html

I'll finish that tomorrow.

Regards,

Nikola Boychev

User Gravatar

NBoychev

Posted Mar 12 2014 16:34 UTC

Hi Kevin,

Here is the code:

HTML:

<html ng-app="dndApp" class="ng-scope"><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><style type="text/css">@charset "UTF-8";[ng:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng:form{display:block;}</style>
    <title>Drag Drop iFrame using Angular JS Directive</title>
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">

    <style>
        #tools-group li{float:left;width:25%;padding-right:5px;list-style: none;height:60px;border: 1px solid #000;margin-right:4px;margin-top:5px;background-color: #efefef}
        #onPageDiv{height:0px;border:1px solid #000000;background-color: #efefef}
        #ifrContainer{height:100%;left:0;

        }
        #ifr{width:100%;height: 100%}
        .drag-active {z-index: 2;-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter: alpha(opacity = 100);opacity: 1;}
        .drop-hover{ border: 4px dashed #51b62b; }




        /* ===========================
            #NEW CSS
        ============================ */

        .droppable-fake { position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: -4; }
        .droppable-fake.drop-active { z-index: 2; }
        .inner-drag .droppable-fake { display: none; }
        .iframe-holder { position: relative; }

        #ifrContainer {
            -webkit-transform: scale(.5);
               -moz-transform: scale(.5);
                -ms-transform: scale(.5);
                 -o-transform: scale(.5);
                    transform: scale(.5);
        }
    </style>
</head>
<body style="">
    <div class="container">
        <div class="row">
            <div class="col-md-8">
               <!-- <h2>Droppable on the page - <span class="alert alert-success">works perfect :-)</span> </h2>
                <div id="onPageDiv" data-greedy="false">

                </div>-->
                <h2>Droppable on iFrame</h2>

                <div class="iframe-holder">
                    <div id="ifrContainer">
                        <iframe id="ifr" src="./Drag Drop iFrame using Angular JS Directive_files/inner.html" data-scale="0.5" dnd-droppable-iframe=""></iframe>
                    </div>

                    <div class="droppable-fake"></div><!-- /.dropppable-fake -->
                </div><!-- /.iframe-holder -->
            </div>
            <div class="col-md-4">
                <h2>Draggables</h2>
                <ul id="tools-group">
                    <li data-raw="&lt;div class=&#39;well&#39; dnd-draggable dnd-droppable&gt;&lt;/div&gt;" data-helper="clone" dnd-draggable="" class="ui-draggable"><i class="glyphicon  glyphicon-adjust"></i>Drag 1</li>
                    <li data-raw="&lt;div class=&#39;alert alert-success&#39; dnd-draggable dnd-droppable&gt;&lt;/div&gt;" data-helper="clone" dnd-draggable="" class="ui-draggable"><i class="glyphicon  glyphicon-adjust"></i>Drag 2</li>
                    <li data-raw="&lt;div class=&#39;alert alert-danger&#39; dnd-draggable dnd-droppable&gt;&lt;/div&gt;" data-helper="clone" dnd-draggable="" class="ui-draggable"><i class="glyphicon  glyphicon-adjust"></i>Drag 3</li>
                    <li data-raw="&lt;div class=&#39;alert alert-warning&#39; dnd-draggable dnd-droppable&gt;&lt;/div&gt;" data-helper="clone" dnd-draggable="" class="ui-draggable"><i class="glyphicon  glyphicon-adjust"></i>Drag 4</li>
                    <li data-raw="&lt;a class=&#39;btn btn-default&#39; dnd-draggable&gt;Button&lt;/a&gt;" data-helper="clone" dnd-draggable="" class="ui-draggable"><i class="glyphicon  glyphicon-adjust"></i>Drag 5</li>
                </ul>
            </div>
        </div>

    </div>
    <script src="./Drag Drop iFrame using Angular JS Directive_files/jquery-2.0.3.min.js"></script>
    <script src="./Drag Drop iFrame using Angular JS Directive_files/jquery-ui.js"></script>
    <script src="./Drag Drop iFrame using Angular JS Directive_files/angular.min.js"></script>
    <script src="./Drag Drop iFrame using Angular JS Directive_files/app.js"></script>




</body></html>

JS:

'use strict';

var dndApp = angular.module('dndApp',[]);

dndApp.directive('dndDraggable', function() {
    return {
        // A = attribute, E = Element, C = Class and M = HTML Comment
        restrict:'A',
        link: function(scope, element, attrs,controller) {
            var helper = attrs.helper;
            var $holder = $('.iframe-holder');
            var $iframe = $holder.find('iframe');
            var $fakeDiv = $holder.find('.droppable-fake');
            var css = {};
            var innerDrag = false;

            var dataScale = $iframe[0].getAttribute('data-scale');

            var scale = dataScale ? parseFloat(dataScale) : 0;

            var offsetX = 0;
            var offsetY = 0;

            if(helper==undefined){
                helper='original';
            }
            element.draggable({
                revert:false,
                helper:'clone',
                iframeFix: true,
                start: function(event, ui){
                    var holderBounds = $holder[0].getBoundingClientRect();
                    var holderTop = holderBounds.top;
                    var holderLeft = holderBounds.left;
                    css = $.extend({}, $iframe[0].getBoundingClientRect());
                    innerDrag = $(event.delegateTarget).closest('#tools-group').length === 0;

                    offsetX = event.offsetX;
                    offsetY = event.offsetY;

                    css.position = 'absolute';
                    css.right = null;
                    css.bottom = null;
                    css.absTop = css.top;
                    css.absLeft = css.left;
                    css.top = css.top - holderTop;
                    css.left = css.left - holderLeft;

                    $fakeDiv.css(css);

                    $holder.toggleClass('inner-drag', innerDrag);


                },
                drag:function (event, ui) {
                    if(innerDrag){
                        if(scale){

                            ui.position.top = ui.position.top / scale - css.absTop / scale;
                            ui.position.left = ui.position.left / scale - css.absLeft / scale + offsetX / scale;
                        } else {
                            ui.position.top -= css.absTop;
                            ui.position.left -= css.absLeft;

                        }
                    }
                },
                stop:function (event, ui) {
                    $(this).removeClass("drag-active").closest(element).removeClass("drag-active");
                    var droppedEl = angular.element(ui.droppable);
                }
            });
        }
    }
});

dndApp.directive('dndDroppableIframe', function($compile) {
    return {
        restrict: 'A',
        link: function(scope,element,attrs){
            if (element.prop('tagName') !== 'IFRAME') { return; }

            element.ready(function () {
                var $parent = element.closest('.iframe-holder');
                var $fakeDiv = $parent.find('.droppable-fake').attr('dnd-droppable', '');


                $compile($fakeDiv)(scope);
            });
        }
    }
});

dndApp.directive('dndDroppable', function($compile) {
    return {
        restrict: 'A',
        link: function(scope,element,attrs,controller){
            var greedy = attrs.greedy;
            var $holder = $('.iframe-holder');
            var ifr = $holder.find('iframe');
            var ifrBody = ifr.contents().find('body');
            var dropDiv = ifrBody.find("#droppable-div");
            var inner = false;

            if(!element.closest('.iframe-holder').length){
                dropDiv = element;
                inner = true;
            };

            if(greedy==undefined){greedy=true;}

            element.droppable({
                activeClass:"drop-active",
                greedy:greedy,
                hoverClass:"drop-hover",
                drop:function (event, ui) {

                    var rawHtml = angular.element(ui.draggable).data('raw');
                    var draggedEl = angular.element(ui.draggable); //.parent();
                    var droppedEl = angular.element(dropDiv);

                    if(rawHtml == undefined){
                        rawHtml = $(draggedEl)[0].outerHTML;
                    }

                    $compile(rawHtml)(scope).appendTo(droppedEl);
                    if(angular.element(ui.draggable).data('helper') == undefined){
                        $(draggedEl).remove();
                    }
                    $holder.removeClass('inner-drag');
                    $('.drop-active').removeClass('drop-active');
                    $('.drop-hover').removeClass('drop-hover');

                    ifrBody.find('.drop-active').removeClass('drop-active');
                    ifrBody.find('.drop-hover').removeClass('drop-hover');
                },
                over: function(event, ui){
                    if(inner){
                        $holder.addClass('inner-drag');
                    };
                },
                out: function(){
                    if(inner){
                        $holder.removeClass('inner-drag');
                    };
                },
                stop: function(){
                    $holder.removeClass('inner-drag');
                    $('.drop-active').removeClass('drop-active');
                    $('.drop-hover').removeClass('drop-hover');

                    ifrBody.find('.drop-active').removeClass('drop-active');
                    ifrBody.find('.drop-hover').removeClass('drop-hover');

                }
            });
        }
    }
});


$.ui.intersect = function(draggable, droppable, toleranceMode) {

    if (!droppable.offset) {
        return false;
    }

    var droppableBounds = droppable.element[0].getBoundingClientRect();
    var elementDoc = droppable.element[0].ownerDocument;
    var elementWin = elementDoc.defaultView || elementDoc.parentWindow;
    var parentBounds = {
        top: 0,
        bottom: 0,
        left: 0,
        right: 0,
        width: 0,
        height: 0
    };
    var scale = 1;

    if(elementWin !== top){
        var frameElement = elementWin.frameElement;
        var dataScale = frameElement.getAttribute('data-scale');
        parentBounds = frameElement.getBoundingClientRect();

        if(dataScale){
            scale = dataScale;
        };
    };

    var draggableLeft, draggableTop,
        x1 = (draggable.positionAbs || draggable.position.absolute).left,
        x2 = x1 + draggable.helperProportions.width,
        y1 = (draggable.positionAbs || draggable.position.absolute).top,
        y2 = y1 + draggable.helperProportions.height,
        l = droppableBounds.left * scale + parentBounds.left,
        r = l + droppable.proportions.width * scale,
        t = droppableBounds.top * scale + parentBounds.top,
        b = t + droppable.proportions.height * scale;

    switch (toleranceMode) {
        case "fit":
            return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
        case "intersect":
            return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
                x2 - (draggable.helperProportions.width / 2) < r && // Left Half
                t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
                y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
        case "pointer":
            draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
            draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
            return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
        case "touch":
            return (
                (y1 >= t && y1 <= b) || // Top edge touching
                (y2 >= t && y2 <= b) || // Bottom edge touching
                (y1 < t && y2 > b)      // Surrounded vertically
            ) && (
                (x1 >= l && x1 <= r) || // Left edge touching
                (x2 >= l && x2 <= r) || // Right edge touching
                (x1 < l && x2 > r)      // Surrounded horizontally
            );
        default:
            return false;
        }

};

Please note that you have to add data attribute:

data-scale="0.5"

To the iframe, if the parent is scaled to 50%. (it will work with any scale)

The updated files are:

HTML (outer and inner) and app.js

Regards,

Nikola Boychev

User Gravatar

NBoychev

Posted Mar 14 2014 15:39 UTC

Solution

This didn't solve your task? Get your own custom solution.

Hi Nikola

How to draggable and droppable existing component in iframe, Or All existing ifrmae components are draggable and droppable

How to achieve this situation? Please help me.

Preview or Download code:

http://plnkr.co/edit/Me3Chi?p=info

Thanks

Mohit

User Gravatar

mohit0313

Posted Jun 18 2014 7:28 UTC

Hi mohit0313,

I'm not sure that I understand your issue. Could you please clarify?

Regards,

Nikola Boychev

User Gravatar

NBoychev

Posted Jun 19 2014 11:56 UTC

Add a reply

By posting a reply on CodersClan you agree to our Terms & Conditions