jmasci/html-element

此软件包最新版本(dev-master)没有提供许可信息。

纯HTML元素渲染函数;以及可变HTML元素对象。

dev-master 2020-02-28 05:09 UTC

This package is auto-updated.

Last update: 2024-09-16 02:47:38 UTC


README

将HTML渲染库拆分为两个(几乎)非耦合的部分

El类提供了将元素属性转换为HTML的纯方法。您可以单独使用它。

Element类管理元素属性,然后使用El进行渲染。您可以轻松地替换自己的渲染机制。

1. El类

基本用法

use JMasci\HtmlElement\El;

echo El::get( 'p', "inner", [
    'class' => 'class-1',    
] );
<p class="class-1">...</p>

El::open、定义类/ID的标签以及将类作为列表的演示

echo El::open( 'div.class-1#id', [
    'class' => [ 'class-2', 0 ? 'class-3' : '' ]
] );
echo "inner";
echo El::close( 'div' );
<div id="id" class="class-1 class-2">inner</div>

自闭合标签、将类作为布尔值的字典、JSON编码辅助工具以及仅键属性(例如,必需的)

echo El::open( 'input', [
    'type' => 'number',
    'name' => 'number',            
    'class' => [ 'class-1' => true, 'class-2' => false ],    
    'min' => 0,    
    'step' => 1,    
    'data-json' => El::json_encode_for_html_attr( $some_array_or_object ),        
    'required' => true,
] );
<input type="number" name="number" class="class-1" min="0" step="1" data-location="{json...}" required />

用户输入/净化/扩展性

El::get() 和 El::open() 将过滤、验证和净化您传入的所有数据(除了内部HTML)。

还有 El::get_strict() 和 El::open_strict(),它不会执行上述任何操作。它期望

  • 所有数据都经过净化
  • 所有属性都是字符串。
  • 标签是有效的标签,且不包含ID或类

您还可以使用用于过滤/验证/净化您的数据的这些方法。您可以使用它们的一个子集,并包装“严格”方法来定义自己的解决方案,以更好地满足您的需求。如果您的数据以您不喜欢的其他方式净化,这可能很有用。扩展该类也是可能的。有关更多信息,请参阅代码;它考虑了可扩展性。

1. Element类

基本用法

use JMasci\HtmlElement\Element;

$element = new Element( 'div', [ 'class' => 'container' ], [
    new Element( 'p', [], [
        "Text"
    ] )
] );

echo $element->render();
<div class="container"><p>Text</p></div>

非基本用法。演示了大多数可用方法,但不是全部;请参阅代码以获取更多信息。

use JMasci\HtmlElement\Element;

$element = new Element( null );

// set methods normally return $element
$element->tag_set( 'div' )->attr_set( 'id', 'main' );

$element->attr_get( 'id' ); # "main"
$element->tag_get(); # "div"
$element->attr_exists( 'data-test' ); # false
$element->attr_set( 'data-test' );
$element->attr_reset( 'data-test' );
$element->attr_exists( 'data-test' ); # true
$element->attr_delete( 'data-test' );
$element->attr_exists( 'data-test' ); # false

// all methods are potentially variadic. add_class may accept multiple
// parameters or arrays.
$element->add_class( 'class-1' )->add_class( 'class-2' );
$element->has_class( 'class-1' ); # true
$element->remove_class( 'class-2' );
$element->attr_get( 'class' ); # "class-1"

// different attributes know how to handle different operations.
// the id attribute does not understand "add". The style attribute does.
$element->add_style('display: none;');

// there are many ways to do the same thing. This is not a feature,
// but a result of the extensibility that is built-in. You should
// generally avoid using these methods unless you know what you are doing.
// mostly, you'll use them when extending the class to add your own functionality.
$element->_compound_attr_add( 'style', 'color: red;');
$element->attr_get_instance( 'style' )->add( 'color: blue;' );
$element->attr_get_instance( 'style' )->_call_dynamic_method( 'add', 'color: green;' );

// this will result in an error, unless you extended the 'id' attribute to define 'has'. 
// $element->attr_get_instance( 'id' )->_call_dynamic_method( 'has', '...' );

// adding child elements. note: nesting depth of children is unlimited.
$element->child_append( new Element('p', [], [ "Paragraph 1" ] ));

// stores a string as a child. It is not converted into an Element.
$element->child_append( "<p>Paragraph 2</p>");

// prepend a child
$element->child_prepend( new Element('p', [], [ "Paragraph 0" ] ));

// a "fragment" contains only children.
// we created a new element containing 2 children which are the same.
// note: $element is not cloned, its a reference. Considering cloning if needed.
$fragment = Element::get_fragment_instance( [ $element, $element ] );
$fragment->child_prepend( '<p>Before...</p>' );
echo $fragment->render();

结果如下

<p>Before...</p>
<div id="main" class="class-1" style="display: none; color: red; color:blue; color: green;">
    <p>Paragraph 0</p>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
</div>
<div id="main" class="class-1" style="display: none; color: red; color:blue; color: green;">
    <p>Paragraph 0</p>
    <p>Paragraph 1</p>
    <p>Paragraph 2</p>
</div>

真实世界示例

注意:我不建议您总是使用Element来编写HTML;这样做可能会导致代码混乱。

然而,在某些情况下,缺乏灵活性并不是一个选项。因此,我们可以用一点简单性来换取大量的灵活性。

use JMasci\HtmlElement\Element;

// a function that returns Element instances and a function that knows how to render them.
// this lets the Element's be modified before rendering. You can use the same idea
// in a lot of different ways.
function get_the_thing(){
    
    $wrapper = new Element( 'div', [ 'class' => 'wrapper' ] );
    $container = new Element( 'div', [ 'class' => 'container' ] );    

    $render = function( $wrapper, $container ){
        ob_start();                        
        echo $wrapper->open_tag();
        echo $container->open_tag();
        echo '...';
        echo $container->close_tag();    
        echo $wrapper->close_tag();        
        // this would have the same effect
        // echo $wrapper->append_child( $container->append_child( "..." )->render();
        return ob_get_clean();                    
    };
    
    return [ $wrapper, $container, $render ];
}

list( $wrapper, $container, $render ) = get_the_thing();

$wrapper->add_class( 'wide-wrapper' );

$container->before( "<h1>Above Container Title</h1>");

echo $render( $wrapper, $container );
<div class="wrapper wide-wrapper">
    <h1>Above Container Title</h1>
    <div class="container">...</div>
</div>

限制/不支持

您会注意到许多方法看起来与jQuery中的方法类似。虽然您可以更新所有元素的属性,但不能查询元素的子元素。

例如,没有 $element->querySelector( 'input[name="first_name"]' )。虽然这当然可以实现,但我目前没有计划实现它。

此外,无法从HTML字符串创建Element实例。这个过程并不简单。

ElementAttribute / 扩展性

每个Element都包含一个ElementAttribute实例数组。

ElementAttribute具有可覆盖的匿名函数来访问它们的值。

例如,'class'属性定义了'get'、'set'、'add'、'remove'、'has'。

默认情况下,style属性定义了'get'、'set'、'add'。

所有其他属性都使用默认实例(但您可以更改它)。默认实例仅定义'get'和'set'。

注意:动态'get'方法应始终返回标量值。它在渲染期间被调用。

此外,所有动态方法都可以接受可变数量的参数。

一般来说,很难知道哪些属性定义了哪些动态方法。同样,也很难知道这些动态方法的参数。这不是理想的,但这是为了灵活性而必须做出的权衡。为了处理这个事实,您可以通过扩展ElementBaseClass或Element来创建自己的定义良好的包装方法。

use JMasci\HtmlElement\Element;
use JMasci\HtmlElement\ElementAttribute;

Class MyElement extends Element{
    
    // override this to modify attribute instances
    protected function build_attribute_instance($name){
    
        $attr = parent::build_attribute_instance( $name );        
        
        // define your own 'remove' method on the 'style' attribute.
        if ( $name === 'style' ) {
            $attr->methods['remove'] = function( ...$args ) {
                // note: "$this" will be the ElementAttribute instance.                               
            };                          
        }
               
        return $attr;    
    }
}

$e = new MyElement( 'p' );

// "style" now understands "remove"
$e->attr_get_instance( 'style' )->_call_dynamic_method( 'remove', 'display' );