javascriptmootools

How can I inject a string of HTML into an element?


Using Mootools, we can inject an element into another element:

$('childID').inject($('parentID'), 'top');

The second parameter allows me to control the location and can either be 'top' or 'bottom' to inject it into a parent object or 'before' or 'after' to inject it as a sibling.

We can also set the HTML of an element from a string:

var foo = "<p>Some text</p>";
$('parentID').set('html', foo);

My problem is that I want to have the same flexibility with strings as I do with elements. I can't, for example, put a string at the top of an element using set() as this overwrites the HTML rather than appending it at a specific location. Similarly, I can't append HTML after or before a sibling element.

Is there a function that will allow me to inject strings in the same way as I inject elements?


Solution

  • Best Solution

    The inject method will look like this:

    inject: function(element, location) {
        var el = Elements.from(this);
    
        if($type(el) === 'array') var el = el.reverse();
    
        return el.inject(element, location);
    }
    

    Let's break this into parts.

    1) Elements.from(this) will take whatever the method is applied to and convert it into elements:

    var foo = "<p>Some text</p>";
    var el = Elements.from(foo);
    //el is equal to a p element. 
    
    var bar = "<div>First div</div><div>Second div</div>";
    var el = Elements.from(bar);
    //el is equal to an array containing 2 div elements
    

    2) if($type(el) === 'array') checks if el is an array. If it is then it applies .reverse() to el. This is necessary to inject the elements in the correct order. Otherwise they would inject with, for example, the second div first and the first div second. Obviously if el is just a single element, we don't need to change its order.

    3) Finally, we just use the original inject method to inject el into the element specified in the element parameter to the location specified in the location parameter. If el is an array of elements, they will all get injected just fine.

    To be able to use this function, we have to add it as a method on string objects. To do this you have to use implement():

    String.implement({
        inject: function(element, location) {
            var el = Elements.from(this);
    
            if($type(el) === 'array') var el = el.reverse();
    
            return el.inject(element, location);
        }
    });
    

    This will allow you to use the inject function on any variable containing a string. Make sure you don't put this inside the domready event i.e. Before window.addEvent('domready', function() { ... });

    Now the inject function itself will look like this:

    var foo = "<p>Some text</p>";
    foo.inject($('parentID'), 'top');
    

    This will create the p element and inject it at the top of parentID.

    Alternative Solution

    If you just wish to use inject with the 'top' and 'bottom' locations, you can use this inject method instead:

    inject: function(element, location) {
        var html = element.get('html')
    
        if(location === 'top') return element.set('html', this + html);
        else if (location === 'bottom') return element.set('html', html + this);
    }
    

    This method will get the innerHTML of the element you need to convert and either concatenate the string with that HTML or the HTML with that string, placing the string at the top or the bottom of the element respectively. The element's innerHTML is then set to this value.

    The advantage of this method is that as long as the innerHTML of the element isn't too great, this is likely to be faster as we don't need to create new elements which could be time-consuming if the string contains many top-level sibling elements. Obviously if this situation were reversed (few top-level siblings and small innerHTML), the speed advantage would also be reversed (I haven't tested the speed difference so this is just an educated guess and might be negligible).

    The disadvantage, however, is that we can't easily use it with the 'after' and 'before' locations.